关于单下划线和双下划线:两者都表示相同的“隐私”概念。也就是说,人们会知道该属性(无论是方法还是“普通”数据属性或其他任何内容)不是对象的公共 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
and b._single
并得到_single
属性创建者ClassA
:
>>> a._single, b._single
('Single', 'Single')
>>> a.getSingle(), b.getSingle(), b.getSingle_B()
('Single', 'Single', 'Single')
但试图访问__double
属性上的a
or 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
当在类内部访问属性名称时,属性名称将被名称修改以包含正在访问的类的名称in. When ClassA
尝试访问self.__double
,它实际上在编译时变成了对self._ClassA__double
,同样对于ClassB
。 (如果方法在ClassB
被分配给__double
,为了简洁起见,未包含在代码中,因此不会触及ClassA
's __double
但创建一个新属性。)该属性没有其他保护,因此如果您知道正确的名称,您仍然可以直接访问它:
>>> a._ClassA__double, b._ClassA__double
('Double', 'Double')
那么为什么这是一个问题呢?
好吧,每当您想要继承和更改处理此属性的任何代码的行为时,这都是一个问题。您要么必须重新实现直接触及此双下划线属性的所有内容,要么必须猜测类名并手动修改名称。当这个双下划线属性实际上是一个方法时,问题会变得更糟:重写该方法或者调用子类中的方法意味着手动进行名称修改,或者重新实现调用该方法的所有代码以不使用双下划线名称。更不用说动态访问属性了getattr()
:你也必须手动破坏那里。
另一方面,由于该属性仅被简单地重写,因此它仅提供表面的“保护”。任何代码片段仍然可以通过手动修改来获取该属性,尽管这会使their代码取决于名称your类,而您重构代码或重命名类(同时仍然保持相同的用户可见名称,这是 Python 中的常见做法)的努力将不必要地破坏他们的代码。他们还可以通过将他们的类命名为与你的类相同的名称来“欺骗”Python 来为他们进行名称修改:请注意,修改后的属性名称中没有包含模块名称。最后,双下划线属性在所有属性列表和所有形式的内省中仍然可见,这些内省不注意跳过以 ( 开头的属性single) 下划线。
So, if您使用双下划线名称,并且非常谨慎地使用它们,因为它们可能会变得非常不方便,并且永远不要将它们用于方法或子类可能想要重新实现、覆盖或直接访问的任何其他内容。并意识到双前导下划线名称修改提供了没有真正的保护。最后,使用一个前导下划线会给你带来同样多的好处,并给你带来更少的(潜在的、未来的)痛苦。使用单个前导下划线。