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