Что касается одинарного и двойного нижнего подчеркивания: оба указывают на одно и то же понятие «конфиденциальность». Другими словами, люди будут знать, что атрибут (будь то метод, «обычный» атрибут данных или что-то еще) не является частью общедоступного API объекта. Люди будут знать, что прикоснуться к ней напрямую — значит навлечь беду.
Кроме того, атрибуты подчеркивания с двойным началом (но не атрибуты с одинарным подчеркиванием) искажены именами, чтобы сделать доступ к ним случайным из подклассов или где-либо еще за пределами текущего класса менее вероятно. Вы все еще можете получить к ним доступ, но не так тривиально. Например:
>>> class ClassA:
... def __init__(self):
... self._single = "Single"
... self.__double = "Double"
... def getSingle(self):
... return self._single
... def getDouble(self):
... return self.__double
...
>>> class ClassB(ClassA):
... def getSingle_B(self):
... return self._single
... def getDouble_B(self):
... return self.__double
...
>>> a = ClassA()
>>> b = ClassB()
Теперь вы можете тривиально получить доступ к a._single и b._single и получить атрибут _single, созданный ClassA:
>>> a._single, b._single
('Single', 'Single')
>>> a.getSingle(), b.getSingle(), b.getSingle_B()
('Single', 'Single', 'Single')
Но попытка прямого доступа к атрибуту __double экземпляра a или b не сработает:
>>> a.__double
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: ClassA instance has no attribute '__double'
>>> b.__double
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: ClassB instance has no attribute '__double'
И хотя методы, определенные в ClassA, могут обращаться к нему напрямую (при вызове в любом экземпляре):
>>> a.getDouble(), b.getDouble()
('Double', 'Double')
Методы, определенные в ClassB, не могут:
>>> b.getDouble_B()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 5, in getDouble_B
AttributeError: ClassB instance has no attribute '_ClassB__double'
И прямо в этой ошибке вы получаете подсказку о том, что происходит. Имя атрибута __double, доступ к которому осуществляется внутри класса, искажается, чтобы включить имя класса, в к которому осуществляется доступ. Когда ClassA пытается получить доступ к self.__double, он фактически превращается — во время компиляции — в доступ к self._ClassA__double, и то же самое для ClassB. (Если бы метод в ClassB был присвоен __double, не включенному в код для краткости, он не затронул бы __double ClassA, а создал бы новый атрибут.) Никакой другой защиты этого атрибута нет, поэтому вы все равно можете получить к нему прямой доступ. если вы знаете правильное имя:
>>> a._ClassA__double, b._ClassA__double
('Double', 'Double')
Так почему это проблема?
Ну, это проблема каждый раз, когда вы хотите наследовать и изменить поведение любого кода, имеющего дело с этим атрибутом. Вы либо должны переопределить все, что напрямую касается этого атрибута двойного подчеркивания, либо вам нужно угадать имя класса и изменить имя вручную. Проблема усугубляется, когда этот атрибут двойного подчеркивания на самом деле является методом: переопределение метода или вызов метода в подклассе означает изменение имени вручную или повторную реализацию всего кода, вызывающего метод, для не используйте имя с двойным подчеркиванием. Не говоря уже о динамическом доступе к атрибуту с помощью getattr(): здесь вам тоже придется манипулировать вручную.
С другой стороны, поскольку атрибут лишь тривиально переписывается, он обеспечивает лишь поверхностную «защиту». Любой фрагмент кода по-прежнему может получить атрибут, изменив его вручную, хотя это сделает код их зависимым от имени вашего класса и усилий с вашей стороны по рефакторингу вашего code или переименовать свой класс (при сохранении того же имени, видимого пользователю, что является обычной практикой в Python) без необходимости нарушило бы их код. Они также могут «обмануть» Python, заставив его изменить имя за них, назвав свой класс таким же, как ваш: обратите внимание, что имя модуля не включено в измененное имя атрибута. И, наконец, атрибут двойного подчеркивания по-прежнему виден во всех списках атрибутов и во всех формах самоанализа, которые не заботятся о пропуске атрибутов, начинающихся с (одинарного) подчеркивания.
Итак, если вы используете имена с двойным подчеркиванием, используйте их очень экономно, так как они могут оказаться весьма неудобными, и никогда не используйте их для методов или чего-либо еще, что подкласс может когда-либо захотеть перереализовать, переопределить или получить доступ напрямую. И помните, что искажение имен с двойным подчеркиванием не дает настоящей защиты. В конце концов, использование одного начального подчеркивания принесет вам столько же пользы и принесет вам меньшую (потенциальную, будущую) боль. Используйте одно ведущее подчеркивание.
person
Thomas Wouters
schedule
03.10.2008