Привет, Habr. Недавно угорел по дизайну — модификаторам доступа и интерфейсам, потом перенес это на язык программирования Python. Прошу под кат — делюсь результатами и как это работает. Для заинтересовавшихся в конце статьи есть ссылка на проект на Github.

Модификаторы доступа


Модификаторы доступы ограничивают доступ объектам — к методам их класса, или дочерним классам — к методам их класса-родителя. Использование модификаторов доступа помогает сокрыть данные в классе таким образом, чтобы никто снаружи не смог помешать работе этого класса.

private (приватные) методы доступны только внутри класса, protected (защищенные) — внутри класса и в дочерних классах.

Как реализованы приватные и защищенные методы в Python


Спойлер — на уровне соглашения, что взрослые люди просто не будут их вызывать вне класса. Перед приватными методами нужно писать двойное нижнее подчеркивание, перед защищенными одно. И вы по-прежнему можете обратиться к методам, несмотря на их «ограниченный» доступ.

class Car:

    def _start_engine(self):
        return "Engine's sound."

    def run(self):
        return self._start_engine()


if __name__ == '__main__':
    car = Car()

    assert "Engine's sound." == car.run()
    assert "Engine's sound." == car._start_engine()

Можно определить следующие минусы:

  • Если бы метод _start_engine обновлял какие-то переменные класса или сохранял состояние, а не просто возвращал «тупой расчет», вы могли что-то поломать для будущей работы с классом. Вы не позволяете себе что-то чинить в моторе вашей машины, потому что тогда никуда не поедете, верно?
  • Вытекающий пункт из предыдущего — чтобы убедиться, что можно «безопасно» (вызов метода не навредит самому классу) использовать защищенный метод — нужно заглянуть в его код и потратить время.
  • Авторы библиотек рассчитывают, что никто не пользуется защищенными и приватными методами классов, которые вы используете в своих проектах. Поэтому могут в любой релиз изменить его реализацию (которая на публичные методы не повлияет из-за обратной совместимости, но вы — пострадаете).
  • Автор класса, ваш коллега, рассчитывает, что вы не увеличите технический долг проекта, использовав защищенный или приватный метод вне созданного им класса. Ведь тому, кто будет его (приватный метод класса) рефакторить или изменять, придется убедиться (например, через тесты), что его изменения не поломают ваш код. А если поломают — ему нужно будет тратить время на то, чтобы решить эту проблему (костылем, потому что надо на вчера).
  • Возможно, вы следите за тем, чтобы другие программисты не использовали защищенные или приватные методам на code review и «бьете за это по рукам», значит — тратите время.

Как реализовать защищенные методы с помощью библиотеки


from accessify import protected


class Car:

    @protected
    def start_engine(self):
        return "Engine's sound."

    def run(self):
        return self.start_engine()


if __name__ == '__main__':
    car = Car()

    assert "Engine's sound." == car.run()

    car.start_engine()

Попытавшись вызвать метод start_engine за пределами класса, вы получите следующую ошибку (метод недоступен согласно политике доступа):

Traceback (most recent call last):
  File "examples/access/private.py", line 24, in <module>
    car.start_engine()
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/accessify/main.py", line 92, in private_wrapper
    class_name=instance_class.__name__, method_name=method.__name__,
accessify.errors.InaccessibleDueToItsProtectionLevelException: Car.start_engine() is inaccessible due to its protection level

Используя библиотеку:

  • Вам не надо использовать некрасивое (субъективно) нижнее или двойное нижнее подчеркивание.
  • Получаете красивый (субъективно) метод внедрения модификаторов доступа в код — декораторы private и protected.
  • Перекладываете ответственность с человека на интерпретатор.

Как это работает:

  1. Декоратор private или protected — самый «высоко» расположенный декоратор, срабатывает до метода класса, которому объявили приватный или защищенный модификатор доступа.


  2. В декораторе с помощью встроенной библиотеки inspect достается текущий объект из стека вызовов — inspect.currentframe(). У этого объекта есть следующие полезные нам атрибуты: пространство имен (locals) и ссылка на предыдущий объект из стека вызова (объект, который вызывает метод с модификатором доступа).


    (Очень упрощенная иллюстрация)
  3. inspect.currentframe().f_back — используем этот атрибут, чтобы проверить, находится ли предыдущий объект из стека вызова в теле класса или нет. Для этого смотрим на пространство имен — f_locals. Если атрибут self в пространстве имен есть — метод вызывается внутри класса, если нет — вне класса. Если вызывать метод с приватным или защищенным модификатором доступа вне класса — будет ошибка политики доступа.

Интерфейсы


Интерфейсы — это контракт взаимодействия с классом, который его имплементирует. Интерфейс содержит в себе сигнатуры методов (название функций, входящие аргументы), а класс, который имплементирует интерфейс — следуя сигнатурам, реализовывает логику. Суммируя, если два класса реализовывают один и тот же интерфейс, вы можете быть уверенным, что у обоих объектов этих классов одинаковые методы.

Пример


Имеем класс User, который использует объект storage, чтобы создать нового пользователя.

class User:

    def __init__(self, storage):
        self.storage = storage

    def create(self, name):
        return storage.create_with_name(name=name)

Сохранять пользователя можно в базу данных, используя DatabaseStorage.create_with_name.

class DatabaseStorage:

    def create_with_name(self, name):
        ...

Сохранять пользователя можно в файлы, используя FileStorage.create_with_name.

class FileStorage:

    def create_with_name(self, name):
        ...

За счет того, что сигнатуры методов create_with_name (название, аргументы) у классов одинаковые — классу User не стоит волноваться какой объект ему подставили, если у обоих одинаковые методы. Это может быть достигнуто если классы FileStorage и DatabaseStorage реализуют одинаковый интерфейс (то есть связаны контрактом определить какой-то метод с логикой внутри).

if __name__ == '__main__':

    if settings.config.storage = FileStorage:
        storage = FileStorage()

    if settings.config.storage = DatabaseStorage:
        storage = DatabaseStorage()

    user = User(storage=storage)
    user.create_with_name(name=...)

Как работать с интерфейсами с помощью библиотеки


Если класс имплементирует интерфейс, класс должен содержать все методы интерфейса. В примере ниже интерфейс «HumanInterface» содержит метод «eat», а класс «Human» его имплементирует, но не реализовывает метод «eat».

from accessify import implements


class HumanInterface:

    @staticmethod
    def eat(food, *args, allergy=None, **kwargs):
        pass


if __name__ == '__main__':

    @implements(HumanInterface)
    class Human:

        pass

Скрипт завершит работу со следующей ошибкой:

Traceback (most recent call last):
  File "examples/interfaces/single.py", line 18, in <module>
    @implements(HumanInterface)
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/accessify/interfaces.py", line 66, in decorator
    interface_method_arguments=interface_method.arguments_as_string,
accessify.errors.InterfaceMemberHasNotBeenImplementedException: class Human does not implement interface member HumanInterface.eat(food, args, allergy, kwargs)

Если класс имплементирует интерфейс, класс должен содержать все методы интерфейса, включая все входящие аргументы. В примере ниже интерфейс «HumanInterface» содержит метод «eat», который на вход принимает 4 аргумента, а класс «Human» его имплементирует, но реализовывает метод «eat» только с 1 аргументом.

from accessify import implements


class HumanInterface:

    @staticmethod
    def eat(food, *args, allergy=None, **kwargs):
        pass


if __name__ == '__main__':

    @implements(HumanInterface)
    class Human:

        @staticmethod
        def eat(food):
            pass

Скрипт завершит работу со следующей ошибкой:

Traceback (most recent call last):
  File "examples/interfaces/single_arguments.py", line 16, in <module>
    @implements(HumanInterface)
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/accessify/interfaces.py", line 87, in decorator
    interface_method_arguments=interface_method.arguments_as_string,
accessify.errors.InterfaceMemberHasNotBeenImplementedWithMismatchedArgumentsException: class Human implements interface member HumanInterface.eat(food, args, allergy, kwargs) with mismatched arguments

Если класс имплементирует интерфейс, класс должен содержать все методы интерфейса, включая входящие аргументы и модификаторы доступа. В примере ниже интерфейс «HumanInterface» содержит приватный метод «eat», а класс «Human» его имплементирует, но не реализовывает приватный модификатор доступа к методу «eat».

from accessify import implements, private


class HumanInterface:

    @private
    @staticmethod
    def eat(food, *args, allergy=None, **kwargs):
        pass


if __name__ == '__main__':

    @implements(HumanInterface)
    class Human:

        @staticmethod
        def eat(food, *args, allergy=None, **kwargs):
            pass

Скрипт завершит работу со следующей ошибкой:

Traceback (most recent call last):
  File "examples/interfaces/single_access.py", line 18, in <module>
    @implements(HumanInterface)
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/accessify/interfaces.py", line 77, in decorator
    interface_method_name=interface_method.name,
accessify.errors.ImplementedInterfaceMemberHasIncorrectAccessModifierException: Human.eat(food, args, allergy, kwargs) mismatches HumanInterface.eat() member access modifier.

Класс может имплементировать несколько (количество неограниченно) интерфейсов. Если класс имплементирует несколько интерфейсов, класс должен содержать все методы всех интерфейсов, включая входящие аргументы и модификаторы доступа. В примере ниже класс «Human» реализовывает метод «eat» интерфейса «HumanBasicsInterface», но не реализовывает метод «love» интерфейса «HumanSoulInterface».

from accessify import implements


class HumanSoulInterface:

    def love(self, who, *args, **kwargs):
        pass


class HumanBasicsInterface:

    @staticmethod
    def eat(food, *args, allergy=None, **kwargs):
        pass


if __name__ == '__main__':

    @implements(HumanSoulInterface, HumanBasicsInterface)
    class Human:

        def love(self, who, *args, **kwargs):
            pass

Скрипт завершит работу со следующей ошибкой:

Traceback (most recent call last):
  File "examples/interfaces/multiple.py", line 19, in <module>
    @implements(HumanSoulInterface, HumanBasicsInterface)
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/accessify/interfaces.py", line 66, in decorator
    interface_method_arguments=interface_method.arguments_as_string,
accessify.errors.InterfaceMemberHasNotBeenImplementedException: class Human does not implement interface member HumanBasicsInterface.eat(food, args, allergy, kwargs)

Киллер фича — метод интерфейса может «заявить» какие ошибки должен «бросить» метод класса, который его имплементирует. В примере ниже «заявлено», что метод «love» интерфейса «HumanInterface» должен выбрасывать исключение «HumanDoesNotExistError» и
«HumanAlreadyInLoveError», но метод «love» класса «Human» не «бросает» одно из них.

from accessify import implements, throws


class HumanDoesNotExistError(Exception):
    pass


class HumanAlreadyInLoveError(Exception):
    pass


class HumanInterface:

    @throws(HumanDoesNotExistError, HumanAlreadyInLoveError)
    def love(self, who, *args, **kwargs):
        pass


if __name__ == '__main__':

    @implements(HumanInterface)
    class Human:

        def love(self, who, *args, **kwargs):

            if who is None:
                raise HumanDoesNotExistError('Human whom need to love does not exist')


Скрипт завершит работу со следующей ошибкой:

Traceback (most recent call last):
  File "examples/interfaces/throws.py", line 21, in <module>
    @implements(HumanInterface)
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/accessify/interfaces.py", line 103, in decorator
    class_method_arguments=class_member.arguments_as_string,
accessify.errors.DeclaredInterfaceExceptionHasNotBeenImplementedException: Declared exception HumanAlreadyInLoveError by HumanInterface.love() member has not been implemented by Human.love(self, who, args, kwargs)

Подводя итоги, с помощью библиотеки:

  • Можно имплементировать один или несколько интерфейсов.
  • Интерфейсы комбинируются с модификаторами доступа.
  • Вы получите разделение интерфейсов и абстрактных классов (модуль abc в Python), теперь не надо использовать абстрактные классы как интерфейсы, если вы это делали (я делал).
  • К сравнению с абстрактными классами. Если вы не определили все аргументы метода из интерфейса — получите ошибку, используя абстрактный класс — нет.
  • К сравнению с абстрактными классами. Используя интерфейсы, вы получите ошибку во время создания класса (когда вы написали класс и вызвали файл *.py). В абстрактных классах вы получите ошибку уже на этапе вызова метода объекта класса.

Как это работает:

  1. В декораторе implements с помощью встроенной библиотеки inspect достаются все методы класса и его интерфейсов — inspect.getmembers(). За уникальный индекс метода принимается комбинация его имени и типа (staticmethod, property, и так далее).
  2. А с помощью inspect.signature() — аргументы метода.
  3. Проходим в цикле по всем методам интерфейса, и смотрим: есть ли такой метод (по уникальному индексу) в классе, который реализовывает интерфейс, одинаковые ли входящие аргументы, одинаковые ли модификаторы доступа, реализовывает ли метод объявленные ошибки в методе интерфейса.

Спасибо за внимание к статье. Ссылка на проект на Github.

Комментарии (54)


  1. alemiks
    19.03.2019 13:36
    +1

    Не очень понял, для кого это? Для тех, кто переходит с C++/Java/C# на питон, чтобы не травмировать психику?


  1. axifive
    19.03.2019 13:42
    +1

    Спойлер — на уровне соглашения, что взрослые люди просто не будут их вызывать вне класса.

    Так в любом языке на уровне соглашениия. И в Java и в C# если калитка закрыта все равно можно влезть в огород через забор. (Рефлексия)
    Ничего не имею против, но есть же официальные соглашения, зачем переизобретать велосипед?)


    1. Free_ze
      19.03.2019 13:54

      И в Java и в C# если калитка закрыта все равно можно влезть

      Там эти соглашения контролирует компилятор и среда выполнения, «калитка» нетривиальна и серьезно отличается как синтаксически, так и семантически.


    1. nexus478
      19.03.2019 14:11

      Так в любом языке на уровне соглашениия. И в Java и в C# если калитка закрыта все равно можно влезть в огород через забор. (Рефлексия)

      Раз так, можно и подкоп устроить и вообще делать что захочется. (unsafe)

      Пример таких игрищ с одного доклада
      image


      1. axifive
        19.03.2019 15:04

        Когда вызываешь проперти через точку, вряд ли возникнет ощущение, что ты делаешь какой-то хак

        obj.__value

        Ну незнаю, когда видишь такие поля и методы не возникает же никаких подозрений? наверно что бы возникло ощущение, что ты делаешь какой-то хак нужно что-то такое?):
        obj.__dont_touch_it_if_you_dont_know_what_it_is_____value


        1. Free_ze
          19.03.2019 15:58

          Если без этого можно обойтись, зачем нужен этот синтаксический шум? В PHP с таким же успехом живет и здравствует бесполезный "$".


  1. dmytrostriletskyi Автор
    19.03.2019 13:44

    Применение интерфейсов не зависит от языка программирования — например, для реализации паттерна стратегия.

    С модификаторами доступа — чтобы не тратить время на то, чтобы пофиксить код, который использует приватный метод из твоего класса. Потому что он внезапно упал, когда ты изменил поведение метода.


  1. brick_btv
    19.03.2019 14:01
    +1

    Надо бы уметь отключать это в runtime. Вызовы inspect достаточно дорогое удовольствие.


  1. iskhomutov
    19.03.2019 14:03
    +3

    Поражает как с завидной регулярностью выходцы из других ЯП пытаются привнести в уже состоявшийся язык особенности, к которым они привыкли и не хотят отвыкать.

    чтобы убедиться, что можно «безопасно» (вызов метода не навредит самому классу) использовать защищенный метод — нужно заглянуть в его код и потратить время

    А может просто не использовать защищенный метод? Он же потому защищенным и называется?
    Поэтому могут в любой релиз изменить его реализацию (которая на публичные методы не повлияет из-за обратной совместимости, но вы — пострадаете)

    Если вы завязываете логику работы своего приложения на приватном методе другого приложения и страдаете — это ваши проблемы а не разработчика другого приложения.
    Возможно, вы следите за тем, чтобы другие программисты не использовали защищенные или приватные методам на code review и «бьете за это по рукам», значит — тратите время.

    Нет не тратим — такие вещи покрываются линтерами.
    Вообще, вся эта возня вокруг «защищенных» методов в питоне напоминает шутку про ограничивающую веревку перед пропастью — если ты был настолько туп что зашел за нее и упал — так тебе и надо.


  1. Pydeg
    19.03.2019 14:30
    +1

    Может быть нарушение договоренностей на уровне языка, которые описаны в самом начале соответствующего раздела в документации — это всё-таки зона ответственности тех, кто их нарушает, а не авторов пакетов?


  1. evgenyk
    19.03.2019 14:34
    +1

    Лично я считаю что в Python сделано как раз идеально. ДОступ к методам и атрибутам на уровне соглашений. Почему:
    — Если метод не должен быть по мнению автора испольован вне класса, это сразу видно по имени.
    — Если все-таки нужно вызвать/обратиться снаружи, разработчик легко может это сделать безо всяких извратов.
    — Что в итоге получается — ответственность разработчика.
    Язык скриптовый, исходники все равно лежат под рукой, все можно изменить. Зачем портить разработчику жизнь.
    По моему так! © Винни-Пух


  1. nickolaym
    19.03.2019 15:18
    +1

    "Если вы следите на код-ревью — вы тратите время". А если за этим следит библиотека при заходе в метод каждый раз — то она, видимо, время не тратит?
    Как минимум, фича должна быть отключаемой.


    Стоит помнить, что имя self — это конвенция. Никто не мешает писать методы, принимающие this или ещё что угодно.


    В статических и классовых методах нет self, но правила ограничения доступа могут действовать.


    Из метода класса можно или нельзя давать доступ к защищённым методам объектов данного класса, помимо self. То есть, мы даём или не даём привилегии разработчику класса?


    Конвенция питоньей приватности распространяется не только на классы, но и на модули.
    И не только на функции, но и на любые другие атрибуты.
    Было бы естественно давать по пальцам за обращение к чужим приватным атрибутам, — но для этого надо инструментировать не конкретный метод, а весь класс или модуль...


  1. technic93
    19.03.2019 15:34
    +2

    Но ведь можно просто использовать pylint.


  1. BasicWolf
    19.03.2019 16:09

    В начале, хочу похвалить. Получилась интересная статья — в качестве исследования возможностей языка. Но в продакшн я бы ничего из предложенного брать не стал. И вот почему:
    Вас как видимо смутило, что в Python, которому через пару лет третий десяток стукнет, до сих пор нет реализации модификаторов доступа и интерйфесов в том виде, каком они сделаны в Java / C#.

    И дело даже не в договорённости на уровне языка и его культуры.

    Попытавшись вызвать метод start_engine за пределами класса, вы получите следующую ошибку (метод недоступен согласно политике доступа)

    ОПА! А что нам даёт эта ошибка во время рантайма? В Java / C# это будет ошибка времени компиляции, а не выполнения.
    С другой стороны тот же pylint спокойно отловит доступ к private/protected методам.
    А с третьей — pylint тут и не нужен. Если программист по какой-либо причине использует приватные поля базового класса, или объекта — он должен полностью отдавать себе отчёт в том, что делает.

    @implements(HumanInterface)

    А как же утиная типизация? Есть у объекта метод __iter__ — значит по нему (скорее всего) можно итерироваться тем же for, нету — нельзя.

    Чтобы не забыли? — вот тут соглашусь, это может быть удобно в больших проекта, но…
    Pylint опять спешит на помощь и ловит неимплементированные методы абстракных классов!

    @throws(HumanDoesNotExistError)
    — a наколько глубока интроспекция? Может где-то в глубине метода я вызываю функции, которые эти исключения бросают? А может где-то вылетают и другие исключения?

    Всё вышепредложенное, в Питоне есть на уровне устоявшихся конвенций или может быть реализовано на уровне программ статического анализа кода.


  1. zvirusz
    19.03.2019 16:26
    +2

    Небольшой бенчмарк, показывающий замедление на порядок:


    $ pipenv run python3 -mtimeit -s 'import regular as t; T = t.Test()' 'T.public()'
    2000000 loops, best of 5: 153 nsec per loop
    
    $ pipenv run python3 -mtimeit -s 'import beauty as t; T = t.Test()' 'T.public()'
    100000 loops, best of 5: 2.07 usec per loop

    regular.py
    class Test:
        def public(self):
            return self._private()
    
        def _private(self):
            pass


    1. evgenyk
      19.03.2019 16:38
      +1

      Людям с С++ и Java опытом, следует всегда помнить, что в Python вызов функции это дорогая операция.


      1. zvirusz
        19.03.2019 16:48
        +1

        … как и в примерно любом другом интерпретируемом языке.


  1. evgenyk
    19.03.2019 16:33

    Вообще, даже если оставить Python в стороне, эта фишка (защита полей в классах) в даже в C++ и Java мне не кажется полезной. ИМХО, типичный over-engineering (не знаю как по русски).


    1. Antervis
      19.03.2019 17:44

      «переусложнение». Модификаторы доступа (public/private & co) не нужны когда есть другой языковой механизм разделения интерфейса и реализации, без оверхеда. А разделение интерфейса и реализации необходимо, чтобы не плодились кривые костыли и грязные хаки.


      1. evgenyk
        19.03.2019 19:14

        Я бы перевел, как переусложнение без достаточных на то причин. Увлечение процессом ради самого процесса.


    1. Sirikid
      19.03.2019 18:54

      Согласен, в топку все поля, кроме приватных, а дальше пусть JIT разбирается.


  1. immaculate
    19.03.2019 17:34

    Поздравляю! Вы переизобрели zope.interface. Интерфейсы появились в Zope 15+ лет назад. Воистину, все новое — это забытое старое.


    Вообще, Zope, конечно, значительно опередила время. Потому наверное и умерла — слишком сложно было для многих. Знакомый делал проект на Zope 3, который затем пришлось переписывать на других технологиях, так как по его словам было невозможно найти программистов, способных поддерживать.


    Но до чего же невероятно выглядели Zope и Plone в свое время… Ничто другое даже рядом не стояло. И главное, я не уверен, что даже сейчас какая-нибудь CMS превосходит по возможностям Plone. А уж в во времена расцвета Plone казалось, что эта штука прямиком из далекого будущего.


  1. Griboks
    19.03.2019 19:20

    Если атрибут self в пространстве имен есть — метод вызывается внутри класса, если нет — вне класса.

    А если метод вызывается в другом экземпляре этого класса? Будет выброшено исключение private метода?


  1. gudvinr
    19.03.2019 20:39
    +3

    Как реализованы приватные и защищенные методы в Python
    Спойлер — на уровне соглашения, что взрослые люди просто не будут их вызывать вне класса. Перед приватными методами нужно писать двойное нижнее подчеркивание, перед защищенными одно. И вы по-прежнему можете обратиться к методам, несмотря на их «ограниченный» доступ.

    Это утверждение — в некоторой мере лукавство, которое упускает важную деталь (при учёте которой механизмы библиотеки уже не выглядят такими полезными).
    А деталь вот какая: если вы пишете двойное подчёркивание перед именем метода — это имя манглится и вы уже не сможете снаружи получить доступ напрямую к obj.__private. Случайно или по незнанию вызвать такой метод не получится.
    То есть это больше, чем просто соглашение.


    При этом, принимая во внимания, что получить доступ через obj._classname__private всё-таки можно, стоит учесть что это сделано для того, чтобы избежать перекрытия одинаковых названий функций при наследовании. И вызывая приватный метод таким образом вы сами сознательно подписываетесь, что собираетесь выстрелить себе в ногу. И это точно такой же хак, как и доступ к приватным полям в Java и C#.


    И, насколько я могу судить, принцип манглинга имён не жёстко документирован как обратно совместимый и в теории может быть поломан в будущих версиях языка. Но это уже не стоит принимать за истину, потому что не находил в документации прямого подтверждения этому.


  1. zvulon
    19.03.2019 20:59
    +2

    У вас Java головного мозга, скоро вы IoC в python притащите и AbstractFactory Factory.
    Описаный подход плох, тем что нарушает сразу несколько правил:
    — there is only one way to do it, теперь методы приватные могут быть и без подчеркивания
    — simple is better than complex, зачем тянуть библиотеку во все файлы да еще и добавлять строчку над каждым приватным методом.
    — convention over configuration, в питоне протектед методы начинаются с _ во всех 100500 батареек.

    На последок рекомедную прочитать про from abc import ABC

    that is no pythonic way.


    1. Tishka17
      20.03.2019 08:37

      Не вижу ничего плохого в IoC и DI, в частности (я не про IoC-контейнеры aka DI-фреймворки, если что)


  1. Hellpain
    19.03.2019 22:05
    +1

    не надо в python тащить концепции из java/c#


  1. Tishka17
    20.03.2019 08:34

    По поводу интерфейсов не могу не напомнить, что в питоне утиная типизация. И хочу оставить ссылку на драфт PEP 544, зачатки которого уже есть в модуле typing.


  1. LighteR
    20.03.2019 11:24
    +1

    Забавно, что человек с бэкграундом статически типизированных языков хочет затащить в питон интерфейсы, но при этом полностью игнорирует type hint'ы.



  1. werevolff
    20.03.2019 20:35

    Почему не использовать декоратор вместе с подчёркиванием? Как потом другой питонист будет обслуживать ваш код? Как я понял, декоратор вызывает экзепшн. Но нафига вы вместе с экзепшеном пытаетесь занести в язык кастомный код-стайл? Это в корне неверно. Оставьте этот декоратор — фиг с ним. Но не учите людей отказываться от соглашения. У нас и так достаточно библиотек, которые предлагают кастомный код-стайл. Та же джанга предлагает увеличивать допустимую длину строки до 100-120 cols. DRF в сериалайзерах допускает не имплементировать все методы родителя. Отдельные библиотеки настаивают на кэмл-кейсе в именах ф-ий. Теперь ещё учитывать этот наркоманский синтаксис без подчёркиваний на декораторах?!


    1. microcoder
      20.03.2019 23:28

      Та же джанга предлагает увеличивать допустимую длину строки до 100-120 cols

      А чем это плохо? Как быть программисту у которого монитор >= 27''? Страдать с 80 cols на четверти экрана? Поясните пожалуйста, как Вы работаете с этим соглашением?


      1. werevolff
        21.03.2019 02:49

        Поясните, пожалуйста: для чего вам знать как я работаю с этим? А заодно, поясните: означает ли ваше высказывание, что можно не соблюдать соглашения, если у вас монитор на 27'?


        1. Tishka17
          21.03.2019 10:57
          +1

          Получается, что вместо соглашения о длине строки мы получаем соглашение о размере монитора. Я прям представил contribution guide: минимальный размер монитора 27", не рекомендуется открывать два документа рядом


          1. werevolff
            21.03.2019 10:59

            Python к этому планомерно двигается.


        1. microcoder
          21.03.2019 11:09

          для чего вам знать как я работаю с этим?

          Почему у Вас возникли сложности ответить на простой вопрос? Я хочу знать аргументы, что заставляет вас придерживаться 80 cols и почему вы отказываетесь, например от 100-120 cols.


          означает ли ваше высказывание, что можно не соблюдать соглашения, если у вас монитор на 27'?

          Конечно ДА, ведь это соглашение. С ним можно соглашаться, а можно не соглашаться. Выбор всегда остается за разработчиком. Ничто мне этому не препятствует (Python в Exception не вылетает).


          Меня интересует почему Вы соглашаетесь с этим? Какие аргументы у этого соглашения? У вас монитор 15''? Или что? Можете аргументированно пояснить?


          1. evgenyk
            21.03.2019 12:09

            1) Лично я люблю открыть в emacs несколько документов рядом, обычно 2.
            2) Иногда приходится открывать в терминале и даже с телефона.
            3) Ну и длинные строки провоцируют плохой стиль.
            А тех, кто нарушает общепринятые соглашения, ИМХО, нужно публично расстреливать. Ну на первый раз отрубать мизинец, конечно. Но на третий, обязательно расстреливать!
            Программирование на 70% держится на соглашениях.


            1. microcoder
              21.03.2019 12:17

              Ну первые 2 пунта еще куда не шло, можно принять за аргументы, но вот третий пункт… не аргументирован. Что значит плохой стиль? В чем его "плохость" проявляется? Конкретные примеры есть?


              А тех, кто нарушает общепринятые соглашения, ИМХО, нужно публично расстреливать

              Повеселили )) С большинством соглашений вполне можно согласиться, но вот это соглашения меня просто выбешивает и вынуждает создать аналогичное соглашение, чтобы производители мониторов не производили мониторы диагнональю больше 15'', а кто это соглашение будет нарушать, того, ИМХО:


              нужно публично расстреливать

              ))))


              1. evgenyk
                21.03.2019 13:25

                Причем тут мониторы вообще?
                Если кто-то купил монитор, значит теперь весь мир должен под него подстраиваться?
                Провоцирует пихать в код однострочники и всякие другие плохочитаемые штуки.
                Я сторонник такого кода, при взгляде на который даже не читая особо сразы было бы понятно, что он делает.
                В питоне еще позволяет запихать много отступов, (блоков кода), что тоже ОЧЕНЬ плохой стиль.


                1. microcoder
                  21.03.2019 13:41

                  плохочитаемые штуки
                  было бы понятно, что он делает.
                  ОЧЕНЬ плохой стиль

                  Это эмоции. Аргументов не увидел. Кому-то, например мне, непонятно что код делает, когда в нем присутствуют переносы. Это всё субъективность. Вам то нравится, мне это. Но объективных аргументов получается нет никаких?


                  Причем тут мониторы вообще?

                  При том, чтобы они ограничивали видимую длину строки и способствовали "хорошему" стилю программирования, соответствовали PEP'у.
                  Какое это соглашение будет, хорошее или плохое? Чем оно будет принципиально оличаться от 80 cols?


                  1. evgenyk
                    21.03.2019 13:45

                    Нужно просто следовать соглашениям.


                    1. microcoder
                      21.03.2019 21:02

                      Нужно просто следовать соглашениям.

                      Обнаружило в PEP8 следующее:


                      Many projects have their own coding style guidelines. In the event of any conflicts, such project-specific guides take precedence for that project.

                      Насколько я понял, здесь указано, что PEP не является законом для кода, и если в вашем проекте сущетвуют свои правила, то они в любых конфликтах приоритетней этого PEP.


                      Так что, совершенно не обязательно следовать указанию в 80 cols. Тем более, этот PEP морально устарел, ему уже 18 лет. В 2001 году возможно это было актуально, чтобы код умещался на мониторе 14-15 дюймов которые в те времена были популярны.


                      1. evgenyk
                        21.03.2019 22:36

                        Конечно не обязательно следовать ни PEP ни соглашениям. Даже если бы это не было бы написано.
                        А что обязательно, так это пользоваться софтом написанным без следования общепринятым соглашениям и/или принимать в команду людей, которые не умеют следовать простейшим соглашениям.
                        Это может сберечь много времени и нервов.


                        1. microcoder
                          21.03.2019 23:04

                          принимать в команду людей, которые не умеют следовать простейшим соглашениям.

                          Не понял, при чём тут люди какие-то, если речь шла о PEP? Люди нанимаются и обязаны следовать тому, что принято в команде/компании, а последння не обязана следовать PEP'у и может вполне "законно" создавать свои правила.


                          Изначально речь шла не за людей, а за авторов. Вы же понимаете разницу или нет? Автор может быть компанией, командой, и отдельно взятым Васей которым совершенно не обязательно "Нужно просто следовать соглашениям."


                          Вы же их, почему-то хотите расстреливать ))


                          1. evgenyk
                            22.03.2019 11:37

                            Вы же их, почему-то хотите расстреливать ))


                            Я добрый, но тут без расстрелов не обойтись! :)


      1. immaculate
        22.03.2019 11:18
        +1

        На этот вопрос уже ответили, но я тоже дополню.


        1. Действительно, удобно иметь возможность работать с двумя документами открытыми рядом (vertical split в редакторах/IDE)
        2. Есть такая вещь как 3-way merge без которой часто не обойтись. Длинные строки превращают ее в невыносимый кошмар
        3. Действительно, по-моему опыту, длинные строки приводят к плохому коду… И тяжело читаются даже на большом мониторе. Ведь, когда вы читаете сайт, вам тоже удобно, что строки текста не растянуты на весь экран, а имеют отступы слева и справа, и текст не более определенной ширины. С сайтов, которые используют длиннющие строки во весь экран сразу же хочется уйти, читать их очень сложно. Хорошо, что есть Reader Mode. Но для кода такого пока нет.


        1. microcoder
          22.03.2019 14:33

          Ведь, когда вы читаете сайт, вам тоже удобно, что строки текста не растянуты на весь экран

          Речь не шла про неограниченную длину строки. Конечно, ограниченная строка удобнее, но есть объективные причины для увеличения строки (я использую 140 — очень комфортно на мониторе 27'' с разрешением 2k):


          • Разрешение монитора, размер шрифта, DPI
          • Диагональ матрицы монитора
          • Расстояние наблюдателя до монитора

          Этот пункт PEP'а, на мой взгляд явно устарел, но благо его не нужно оспаривать, перепринимать и вводить новый PEP, так как последний — это всего лишь рекомендация, а предпочтения автора являются приоритетней PEP'а согласно ему же, и в частности пункта про 80 cols.


          А вот другие пункты PEP'а вечны, их вполне можно и нужно соблюдать. У меня претензия только к 80 cols ))


          1. evgenyk
            22.03.2019 14:43

            я использую 140 — очень комфортно на мониторе 27'' с разрешением 2k

            Вы даже не пытаетесь понять. Программы пишутся не для себя и не для компьютера. Программы пишутся для других программистов. А есть ведь еще люди с не очень хорошим зрением…


            1. microcoder
              22.03.2019 14:55

              Люди разные бывают, под всех не подстроишься. Есть те, например, кто использует старый ЭЛТ монитор с 14 дюймами диагонали.


              Программы пишутся для других программистов

              Нет. Программист будет писать то и так, как пожелает автор проекта (автор может быть не только конкретное лицо, но и целая корпорация, которая не пишет "для других программистов", а устанавливает свои правила). В том же yapf можно выбрать разные стили от авторов, включая google-style которые не полностью соответствуют обсуждаемому PEP'у.


  1. sentyaev
    21.03.2019 02:15

    Любой мэинстрим язык в итоге превратиться в Java.


    1. evgenyk
      21.03.2019 13:27

      Это потому, что явистов очень много. А все дурное влияние ужасного монстра — C++.


      1. microcoder
        22.03.2019 14:41

        А все дурное влияние ужасного монстра — C++

        Который вышел из недостатков C


  1. n_elloco
    21.03.2019 13:57

    Перед приватными методами нужно писать двойное нижнее подчеркивание, перед защищенными одно.


    Почему многие в это слепо верят? У двойного подчеркивания совсем другая надобность.


    1. microcoder
      22.03.2019 14:42

      Какая?