Привет, 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.
- Перекладываете ответственность с человека на интерпретатор.
Как это работает:
- Декоратор private или protected — самый «высоко» расположенный декоратор, срабатывает до метода класса, которому объявили приватный или защищенный модификатор доступа.
- В декораторе с помощью встроенной библиотеки inspect достается текущий объект из стека вызовов — inspect.currentframe(). У этого объекта есть следующие полезные нам атрибуты: пространство имен (locals) и ссылка на предыдущий объект из стека вызова (объект, который вызывает метод с модификатором доступа).
(Очень упрощенная иллюстрация)
- 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). В абстрактных классах вы получите ошибку уже на этапе вызова метода объекта класса.
Как это работает:
- В декораторе implements с помощью встроенной библиотеки inspect достаются все методы класса и его интерфейсов — inspect.getmembers(). За уникальный индекс метода принимается комбинация его имени и типа (staticmethod, property, и так далее).
- А с помощью inspect.signature() — аргументы метода.
- Проходим в цикле по всем методам интерфейса, и смотрим: есть ли такой метод (по уникальному индексу) в классе, который реализовывает интерфейс, одинаковые ли входящие аргументы, одинаковые ли модификаторы доступа, реализовывает ли метод объявленные ошибки в методе интерфейса.
Спасибо за внимание к статье. Ссылка на проект на Github.
Комментарии (54)
axifive
19.03.2019 13:42+1Спойлер — на уровне соглашения, что взрослые люди просто не будут их вызывать вне класса.
Так в любом языке на уровне соглашениия. И в Java и в C# если калитка закрыта все равно можно влезть в огород через забор. (Рефлексия)
Ничего не имею против, но есть же официальные соглашения, зачем переизобретать велосипед?)Free_ze
19.03.2019 13:54И в Java и в C# если калитка закрыта все равно можно влезть
Там эти соглашения контролирует компилятор и среда выполнения, «калитка» нетривиальна и серьезно отличается как синтаксически, так и семантически.
nexus478
19.03.2019 14:11Так в любом языке на уровне соглашениия. И в Java и в C# если калитка закрыта все равно можно влезть в огород через забор. (Рефлексия)
Раз так, можно и подкоп устроить и вообще делать что захочется. (unsafe)
Пример таких игрищ с одного докладаaxifive
19.03.2019 15:04Когда вызываешь проперти через точку, вряд ли возникнет ощущение, что ты делаешь какой-то хак
obj.__value
Ну незнаю, когда видишь такие поля и методы не возникает же никаких подозрений? наверно что бы возникло ощущение, что ты делаешь какой-то хак нужно что-то такое?):obj.__dont_touch_it_if_you_dont_know_what_it_is_____value
Free_ze
19.03.2019 15:58Если без этого можно обойтись, зачем нужен этот синтаксический шум? В PHP с таким же успехом живет и здравствует бесполезный "
$
".
dmytrostriletskyi Автор
19.03.2019 13:44Применение интерфейсов не зависит от языка программирования — например, для реализации паттерна стратегия.
С модификаторами доступа — чтобы не тратить время на то, чтобы пофиксить код, который использует приватный метод из твоего класса. Потому что он внезапно упал, когда ты изменил поведение метода.
brick_btv
19.03.2019 14:01+1Надо бы уметь отключать это в runtime. Вызовы inspect достаточно дорогое удовольствие.
iskhomutov
19.03.2019 14:03+3Поражает как с завидной регулярностью выходцы из других ЯП пытаются привнести в уже состоявшийся язык особенности, к которым они привыкли и не хотят отвыкать.
чтобы убедиться, что можно «безопасно» (вызов метода не навредит самому классу) использовать защищенный метод — нужно заглянуть в его код и потратить время
А может просто не использовать защищенный метод? Он же потому защищенным и называется?
Поэтому могут в любой релиз изменить его реализацию (которая на публичные методы не повлияет из-за обратной совместимости, но вы — пострадаете)
Если вы завязываете логику работы своего приложения на приватном методе другого приложения и страдаете — это ваши проблемы а не разработчика другого приложения.
Возможно, вы следите за тем, чтобы другие программисты не использовали защищенные или приватные методам на code review и «бьете за это по рукам», значит — тратите время.
Нет не тратим — такие вещи покрываются линтерами.
Вообще, вся эта возня вокруг «защищенных» методов в питоне напоминает шутку про ограничивающую веревку перед пропастью — если ты был настолько туп что зашел за нее и упал — так тебе и надо.
Pydeg
19.03.2019 14:30+1Может быть нарушение договоренностей на уровне языка, которые описаны в самом начале соответствующего раздела в документации — это всё-таки зона ответственности тех, кто их нарушает, а не авторов пакетов?
evgenyk
19.03.2019 14:34+1Лично я считаю что в Python сделано как раз идеально. ДОступ к методам и атрибутам на уровне соглашений. Почему:
— Если метод не должен быть по мнению автора испольован вне класса, это сразу видно по имени.
— Если все-таки нужно вызвать/обратиться снаружи, разработчик легко может это сделать безо всяких извратов.
— Что в итоге получается — ответственность разработчика.
Язык скриптовый, исходники все равно лежат под рукой, все можно изменить. Зачем портить разработчику жизнь.
По моему так! © Винни-Пух
nickolaym
19.03.2019 15:18+1"Если вы следите на код-ревью — вы тратите время". А если за этим следит библиотека при заходе в метод каждый раз — то она, видимо, время не тратит?
Как минимум, фича должна быть отключаемой.
Стоит помнить, что имя self — это конвенция. Никто не мешает писать методы, принимающие this или ещё что угодно.
В статических и классовых методах нет self, но правила ограничения доступа могут действовать.
Из метода класса можно или нельзя давать доступ к защищённым методам объектов данного класса, помимо self. То есть, мы даём или не даём привилегии разработчику класса?
Конвенция питоньей приватности распространяется не только на классы, но и на модули.
И не только на функции, но и на любые другие атрибуты.
Было бы естественно давать по пальцам за обращение к чужим приватным атрибутам, — но для этого надо инструментировать не конкретный метод, а весь класс или модуль...
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 наколько глубока интроспекция? Может где-то в глубине метода я вызываю функции, которые эти исключения бросают? А может где-то вылетают и другие исключения?
Всё вышепредложенное, в Питоне есть на уровне устоявшихся конвенций или может быть реализовано на уровне программ статического анализа кода.
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.pyclass Test: def public(self): return self._private() def _private(self): pass
evgenyk
19.03.2019 16:33Вообще, даже если оставить Python в стороне, эта фишка (защита полей в классах) в даже в C++ и Java мне не кажется полезной. ИМХО, типичный over-engineering (не знаю как по русски).
Antervis
19.03.2019 17:44«переусложнение». Модификаторы доступа (public/private & co) не нужны когда есть другой языковой механизм разделения интерфейса и реализации, без оверхеда. А разделение интерфейса и реализации необходимо, чтобы не плодились кривые костыли и грязные хаки.
evgenyk
19.03.2019 19:14Я бы перевел, как переусложнение без достаточных на то причин. Увлечение процессом ради самого процесса.
immaculate
19.03.2019 17:34Поздравляю! Вы переизобрели zope.interface. Интерфейсы появились в Zope 15+ лет назад. Воистину, все новое — это забытое старое.
Вообще, Zope, конечно, значительно опередила время. Потому наверное и умерла — слишком сложно было для многих. Знакомый делал проект на Zope 3, который затем пришлось переписывать на других технологиях, так как по его словам было невозможно найти программистов, способных поддерживать.
Но до чего же невероятно выглядели Zope и Plone в свое время… Ничто другое даже рядом не стояло. И главное, я не уверен, что даже сейчас какая-нибудь CMS превосходит по возможностям Plone. А уж в во времена расцвета Plone казалось, что эта штука прямиком из далекого будущего.
Griboks
19.03.2019 19:20Если атрибут self в пространстве имен есть — метод вызывается внутри класса, если нет — вне класса.
А если метод вызывается в другом экземпляре этого класса? Будет выброшено исключение private метода?
gudvinr
19.03.2019 20:39+3Как реализованы приватные и защищенные методы в Python
Спойлер — на уровне соглашения, что взрослые люди просто не будут их вызывать вне класса. Перед приватными методами нужно писать двойное нижнее подчеркивание, перед защищенными одно. И вы по-прежнему можете обратиться к методам, несмотря на их «ограниченный» доступ.Это утверждение — в некоторой мере лукавство, которое упускает важную деталь (при учёте которой механизмы библиотеки уже не выглядят такими полезными).
А деталь вот какая: если вы пишете двойное подчёркивание перед именем метода — это имя манглится и вы уже не сможете снаружи получить доступ напрямую кobj.__private
. Случайно или по незнанию вызвать такой метод не получится.
То есть это больше, чем просто соглашение.
При этом, принимая во внимания, что получить доступ через
obj._classname__private
всё-таки можно, стоит учесть что это сделано для того, чтобы избежать перекрытия одинаковых названий функций при наследовании. И вызывая приватный метод таким образом вы сами сознательно подписываетесь, что собираетесь выстрелить себе в ногу. И это точно такой же хак, как и доступ к приватным полям в Java и C#.
И, насколько я могу судить, принцип манглинга имён не жёстко документирован как обратно совместимый и в теории может быть поломан в будущих версиях языка. Но это уже не стоит принимать за истину, потому что не находил в документации прямого подтверждения этому.
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.Tishka17
20.03.2019 08:37Не вижу ничего плохого в IoC и DI, в частности (я не про IoC-контейнеры aka DI-фреймворки, если что)
Tishka17
20.03.2019 08:34По поводу интерфейсов не могу не напомнить, что в питоне утиная типизация. И хочу оставить ссылку на драфт PEP 544, зачатки которого уже есть в модуле typing.
LighteR
20.03.2019 11:24+1Забавно, что человек с бэкграундом статически типизированных языков хочет затащить в питон интерфейсы, но при этом полностью игнорирует type hint'ы.
axilirator
20.03.2019 12:45Пофиксите DoesNotExists плиз.
ell.stackexchange.com/questions/23943/file-doesn-t-exist-or-file-doesn-t-exists
werevolff
20.03.2019 20:35Почему не использовать декоратор вместе с подчёркиванием? Как потом другой питонист будет обслуживать ваш код? Как я понял, декоратор вызывает экзепшн. Но нафига вы вместе с экзепшеном пытаетесь занести в язык кастомный код-стайл? Это в корне неверно. Оставьте этот декоратор — фиг с ним. Но не учите людей отказываться от соглашения. У нас и так достаточно библиотек, которые предлагают кастомный код-стайл. Та же джанга предлагает увеличивать допустимую длину строки до 100-120 cols. DRF в сериалайзерах допускает не имплементировать все методы родителя. Отдельные библиотеки настаивают на кэмл-кейсе в именах ф-ий. Теперь ещё учитывать этот наркоманский синтаксис без подчёркиваний на декораторах?!
microcoder
20.03.2019 23:28Та же джанга предлагает увеличивать допустимую длину строки до 100-120 cols
А чем это плохо? Как быть программисту у которого монитор >= 27''? Страдать с 80 cols на четверти экрана? Поясните пожалуйста, как Вы работаете с этим соглашением?werevolff
21.03.2019 02:49Поясните, пожалуйста: для чего вам знать как я работаю с этим? А заодно, поясните: означает ли ваше высказывание, что можно не соблюдать соглашения, если у вас монитор на 27'?
microcoder
21.03.2019 11:09для чего вам знать как я работаю с этим?
Почему у Вас возникли сложности ответить на простой вопрос? Я хочу знать аргументы, что заставляет вас придерживаться 80 cols и почему вы отказываетесь, например от 100-120 cols.
означает ли ваше высказывание, что можно не соблюдать соглашения, если у вас монитор на 27'?
Конечно ДА, ведь это соглашение. С ним можно соглашаться, а можно не соглашаться. Выбор всегда остается за разработчиком. Ничто мне этому не препятствует (Python в Exception не вылетает).
Меня интересует почему Вы соглашаетесь с этим? Какие аргументы у этого соглашения? У вас монитор 15''? Или что? Можете аргументированно пояснить?
evgenyk
21.03.2019 12:091) Лично я люблю открыть в emacs несколько документов рядом, обычно 2.
2) Иногда приходится открывать в терминале и даже с телефона.
3) Ну и длинные строки провоцируют плохой стиль.
А тех, кто нарушает общепринятые соглашения, ИМХО, нужно публично расстреливать. Ну на первый раз отрубать мизинец, конечно. Но на третий, обязательно расстреливать!
Программирование на 70% держится на соглашениях.microcoder
21.03.2019 12:17Ну первые 2 пунта еще куда не шло, можно принять за аргументы, но вот третий пункт… не аргументирован. Что значит плохой стиль? В чем его "плохость" проявляется? Конкретные примеры есть?
А тех, кто нарушает общепринятые соглашения, ИМХО, нужно публично расстреливать
Повеселили )) С большинством соглашений вполне можно согласиться, но вот это соглашения меня просто выбешивает и вынуждает создать аналогичное соглашение, чтобы производители мониторов не производили мониторы диагнональю больше 15'', а кто это соглашение будет нарушать, того, ИМХО:
нужно публично расстреливать
))))
evgenyk
21.03.2019 13:25Причем тут мониторы вообще?
Если кто-то купил монитор, значит теперь весь мир должен под него подстраиваться?
Провоцирует пихать в код однострочники и всякие другие плохочитаемые штуки.
Я сторонник такого кода, при взгляде на который даже не читая особо сразы было бы понятно, что он делает.
В питоне еще позволяет запихать много отступов, (блоков кода), что тоже ОЧЕНЬ плохой стиль.microcoder
21.03.2019 13:41плохочитаемые штуки
было бы понятно, что он делает.
ОЧЕНЬ плохой стильЭто эмоции. Аргументов не увидел. Кому-то, например мне, непонятно что код делает, когда в нем присутствуют переносы. Это всё субъективность. Вам то нравится, мне это. Но объективных аргументов получается нет никаких?
Причем тут мониторы вообще?
При том, чтобы они ограничивали видимую длину строки и способствовали "хорошему" стилю программирования, соответствовали PEP'у.
Какое это соглашение будет, хорошее или плохое? Чем оно будет принципиально оличаться от 80 cols?evgenyk
21.03.2019 13:45Нужно просто следовать соглашениям.
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 дюймов которые в те времена были популярны.
evgenyk
21.03.2019 22:36Конечно не обязательно следовать ни PEP ни соглашениям. Даже если бы это не было бы написано.
А что обязательно, так это пользоваться софтом написанным без следования общепринятым соглашениям и/или принимать в команду людей, которые не умеют следовать простейшим соглашениям.
Это может сберечь много времени и нервов.microcoder
21.03.2019 23:04принимать в команду людей, которые не умеют следовать простейшим соглашениям.
Не понял, при чём тут люди какие-то, если речь шла о PEP? Люди нанимаются и обязаны следовать тому, что принято в команде/компании, а последння не обязана следовать PEP'у и может вполне "законно" создавать свои правила.
Изначально речь шла не за людей, а за авторов. Вы же понимаете разницу или нет? Автор может быть компанией, командой, и отдельно взятым Васей которым совершенно не обязательно "Нужно просто следовать соглашениям."
Вы же их, почему-то хотите расстреливать ))
evgenyk
22.03.2019 11:37Вы же их, почему-то хотите расстреливать ))
Я добрый, но тут без расстрелов не обойтись! :)
immaculate
22.03.2019 11:18+1На этот вопрос уже ответили, но я тоже дополню.
- Действительно, удобно иметь возможность работать с двумя документами открытыми рядом (vertical split в редакторах/IDE)
- Есть такая вещь как 3-way merge без которой часто не обойтись. Длинные строки превращают ее в невыносимый кошмар
- Действительно, по-моему опыту, длинные строки приводят к плохому коду… И тяжело читаются даже на большом мониторе. Ведь, когда вы читаете сайт, вам тоже удобно, что строки текста не растянуты на весь экран, а имеют отступы слева и справа, и текст не более определенной ширины. С сайтов, которые используют длиннющие строки во весь экран сразу же хочется уйти, читать их очень сложно. Хорошо, что есть Reader Mode. Но для кода такого пока нет.
microcoder
22.03.2019 14:33Ведь, когда вы читаете сайт, вам тоже удобно, что строки текста не растянуты на весь экран
Речь не шла про неограниченную длину строки. Конечно, ограниченная строка удобнее, но есть объективные причины для увеличения строки (я использую 140 — очень комфортно на мониторе 27'' с разрешением 2k):
- Разрешение монитора, размер шрифта, DPI
- Диагональ матрицы монитора
- Расстояние наблюдателя до монитора
Этот пункт PEP'а, на мой взгляд явно устарел, но благо его не нужно оспаривать, перепринимать и вводить новый PEP, так как последний — это всего лишь рекомендация, а предпочтения автора являются приоритетней PEP'а согласно ему же, и в частности пункта про 80 cols.
А вот другие пункты PEP'а вечны, их вполне можно и нужно соблюдать. У меня претензия только к 80 cols ))
evgenyk
22.03.2019 14:43я использую 140 — очень комфортно на мониторе 27'' с разрешением 2k
Вы даже не пытаетесь понять. Программы пишутся не для себя и не для компьютера. Программы пишутся для других программистов. А есть ведь еще люди с не очень хорошим зрением…microcoder
22.03.2019 14:55Люди разные бывают, под всех не подстроишься. Есть те, например, кто использует старый ЭЛТ монитор с 14 дюймами диагонали.
Программы пишутся для других программистов
Нет. Программист будет писать то и так, как пожелает автор проекта (автор может быть не только конкретное лицо, но и целая корпорация, которая не пишет "для других программистов", а устанавливает свои правила). В том же yapf можно выбрать разные стили от авторов, включая google-style которые не полностью соответствуют обсуждаемому PEP'у.
n_elloco
21.03.2019 13:57Перед приватными методами нужно писать двойное нижнее подчеркивание, перед защищенными одно.
Почему многие в это слепо верят? У двойного подчеркивания совсем другая надобность.
alemiks
Не очень понял, для кого это? Для тех, кто переходит с C++/Java/C# на питон, чтобы не травмировать психику?