Относительно одинарных и двойных подчеркиваний: оба обозначают одно и то же понятие «приватность». То есть люди будут знать, что атрибут (будь то метод, «нормальный» атрибут данных или что-то еще) не является частью общедоступного 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
, не включенный в код для краткости, он не коснулся бы ClassA
__double
, но создал бы новый атрибут.) Другой защиты от этот атрибут, так что вы можете получить к нему прямой доступ, если знаете правильное имя:
>>> a._ClassA__double, b._ClassA__double
('Double', 'Double')
Так почему же это проблема?
Что ж, это проблема в любое время, когда вы хотите унаследовать и изменить поведение любого кода, имеющего дело с этим атрибутом. Вы должны либо переопределить все, что непосредственно касается этого атрибута с двойным подчеркиванием, либо вы должны угадать имя класса и изменить имя вручную. Проблема усугубляется, когда этот атрибут с двойным подчеркиванием на самом деле является методом: переопределение метода или вызов метода в подклассе означает ручное преобразование имени или повторную реализацию всего кода, который вызывает метод, чтобы не используйте двойное подчеркивание Не говоря уже о доступе к атрибуту динамически, с getattr()
: вам также придется вручную там работать.
С другой стороны, поскольку атрибут переписывается только тривиально, он предлагает только поверхностную «защиту». Любой фрагмент кода все еще может получить атрибут, вручную искажая, хотя это сделает их код зависимым от имени вашего класса, и усилия на вашей стороне по рефакторингу вашего кода или переименование вашего класса (при сохранении того же имени, видимого пользователю, что является обычной практикой в Python) без необходимости нарушит их код. Они также могут «обмануть» Python, выполнив для них распределение имен, назвав их класс таким же, как ваш: обратите внимание, что в названии искаженного атрибута нет имени модуля. И наконец, атрибут с двойным подчеркиванием по-прежнему виден во всех списках атрибутов и во всех формах самоанализа, в которых не пропускаются атрибуты, начиная с подчеркивания ( single ).
Итак, , если вы используете имена с двойным подчеркиванием, используйте их крайне редко, так как они могут оказаться довольно неудобными, и никогда не используйте их для методов или чего-либо еще, что подкласс может когда-либо захотеть переопределение, переопределение или прямой доступ . И поймите, что двойное ведение подчеркивания именования предлагает никакой реальной защиты . В конце концов, использование единственного подчеркивания выигрывает у вас столько же и дает вам меньше (потенциальное, будущее) боль. Используйте одно начальное подчеркивание.