<p>关于单前导下划线和双前导下划线:两者都表示相同的“隐私”概念。也就是说,人们会知道属性(无论是方法或“普通”数据属性还是其他什么)不是对象的公共API的一部分。人们会知道,直接触碰它就是招致灾难。</p>
<p>除此之外,双前导下划线属性(而不是单前导下划线属性)是<em>name mangled</em>使从子类或当前类之外的任何地方意外访问它们的可能性降低。你仍然可以访问它们,但不是那么简单。例如:</p>
<pre><code>>>> 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()
</code></pre>
<p>现在您可以简单地访问<code>a._single</code>和<code>b._single</code>,并获取由<code>ClassA</code>创建的<code>_single</code>属性:</p>
<pre><code>>>> a._single, b._single
('Single', 'Single')
>>> a.getSingle(), b.getSingle(), b.getSingle_B()
('Single', 'Single', 'Single')
</code></pre>
<p>但是试图直接访问<code>a</code>或<code>b</code>实例上的<code>__double</code>属性将不起作用:</p>
<pre><code>>>> 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'
</code></pre>
<p>尽管在<code>ClassA</code>中定义的方法可以直接访问它(当在任一实例上调用时):</p>
<pre><code>>>> a.getDouble(), b.getDouble()
('Double', 'Double')
</code></pre>
<p>在<code>ClassB</code>上定义的方法不能:</p>
<pre><code>>>> 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'
</code></pre>
<p>在这个错误中,你得到了一个关于正在发生的事情的提示。当在类中访问时,<code>__double</code>属性名正在被名称损坏,以包括在</em>中访问的类的名称。当<code>ClassA</code>试图访问<code>self.__double</code>时,实际上它在compiletime将变成<code>self._ClassA__double</code>的访问,对<code>ClassB</code>也是如此。(如果<code>ClassB</code>中的方法要分配给<code>__double</code>,为了简洁起见,代码中没有包含该方法,因此它不会触及<code>ClassA</code>的<code>__double</code>,而是创建一个新的属性。)此属性没有其他保护,因此,如果知道正确的名称,您仍然可以直接访问它:</p>
<pre><code>>>> a._ClassA__double, b._ClassA__double
('Double', 'Double')
</code></pre>
<p><strong>那为什么这是个问题?</strong></p>
<p>好吧,当您想继承和更改处理此属性的任何代码的行为时,这都是一个问题。您要么必须直接重新实现与此双下划线属性相关的所有内容,要么必须猜测类名并手动更改名称。当这个双下划线属性实际上是一个方法时,问题变得更糟:重写方法<em>或调用子类</em>中的方法意味着手动损坏名称,或重新实现调用该方法的所有代码以不使用双下划线名称。更不用说使用<code>getattr()</code>动态地访问属性了:您还必须在那里手动地进行管理。</p>
<p>另一方面,由于该属性只是简单地重写,因此它只提供表面的“保护”。任何一段代码仍然可以通过手动损坏来获取属性,尽管这会使<em>它们的</em>代码依赖于<em>您的</em>类的名称,并且您这边重构代码或重命名您的类的努力(同时保持相同的用户可见名称,这是Python中的一种常见做法)将不必要地破坏它们的代码。它们还可以通过将它们的类命名为与您的类相同的名称来“诱骗”Python为它们进行名称损坏:注意损坏的属性名称中没有包含模块名称。最后,双下划线属性在所有属性列表和所有形式的内省中仍然可见,这些内省不需要跳过以(<em>single</em>下划线)开头的属性。</p>
<p>因此,<em>如果</em>使用双下划线名称,请非常谨慎地使用它们,因为它们可能会变得非常不方便,并且永远不要将它们用于方法<strong>或子类可能希望直接重新实现、重写或访问的任何其他项。并认识到双前导下划线名称管理提供了<em>没有真正的保护</em>。最后,使用一个单独的前导下划线可以获得同样多的效果,并减少(潜在的,未来的)痛苦。使用一个前导下划线。</p>