Python中的双下划线

12
class A(object):
    def __get(self):
        pass

    def _m(self):
        return self.__get()


class B(A):
    def _m(self):
        return str(self.__get())

print(A()._m())
print(B()._m())

为什么print(A()._m())会打印None,但是print(B()._m())会抛出AttributeError: 'B' object has no attribute '_B__get'

我以为双下划线可以防止方法覆盖。

如果__get是私有的,那么为什么以下代码可以正常工作?

class A(object):
    def __get(self):
        pass

    def _m(self):
        return self.__get()


class B(A):
    pass

print(A()._m())
print(B()._m())

为什么这段代码不会引发 AttributeError 而且会打印两次 None

2
名称修饰是一种编程技术,用于在类定义中创建私有变量。在Python中,以两个下划线开头的变量名会被自动转换为一个更具描述性的名称,以防止意外访问。但是,这也会导致调用时出现问题。例如,在类B中调用self.__get()实际上是调用self._B__get(),而后者并不存在。除非您想要这种行为,否则不要使用前导双下划线。 - kindall
请查看单个和双下划线在对象名称前的含义以及一些相关链接中的详细解释。 - LinkBerest
关于您的更新:因为您正在从类A中定义的方法调用__get。在支持私有概念的任何语言中,这是完全合法的--实际上,这是私有方法最常见的用例。 - Dale Wilson
2个回答

12

以双下划线开头的名称是私有的(意味着不可由派生类访问)。

这并非绝对可靠,具体实现方式是将名称改写。 Python文档中有提到:

以__spam形式命名的标识符(至少两个前导下划线和最多一个后置下划线)会被文本替换为_classname__spam, 其中classname是当前类名去除前导下划线后的部分。无论标识符的语法位置如何,都会进行此名称混淆,因此它可用于定义私有于此类的实例和类变量、方法、存储在全局范围内的变量,甚至是存储在其他类实例中的变量。

因此,__get实际上在A类中被混淆成了_A__get。当B类尝试引用__get时,它被混淆成了与之不匹配的_B__get

换言之,Xyzzy类中定义的__plugh意味着“除非你正在作为Xyzzy类,否则不要碰__plugh。”


2
"前导双下划线名称是“私有的”(意味着不可用于派生类)。请不要这样做。Python 中没有所谓的私有,这些名称是伪私有而不是私有。" - GIZ
5
前导下划线的目的是隐藏符号,除了定义类之外,其他人都看不到。在许多其他语言中,这个概念被称为“私有”。因此,我使用了一个单词来表示应该被广泛认可的概念。此外,我解释了实现细节,并提到了它缺乏完全保护性。你还有什么其他建议来传达这个概念呢? - Dale Wilson
2
这就是问题所在。单词“private”会暗示着像Java中一样的私有性,即变量无法从类外部访问。你说变量对派生类不可用,这是完全错误的。 - GIZ
2
请注意,我引用的官方Python文档将这个概念称为“类私有”。 - Dale Wilson
建议使用双下划线前缀来表示私有属性,但这并不是强制性的。这正是 @direprobs 的意义所在。也就是说,如果外部(或派生类)可以访问它,那么它就不是真正的私有属性了,虽然已经被设置为难以访问,但并非不可能。 - klaar

3

对于A类的__methodName()成员函数:

  • 从A类外部调用这个成员函数,你只能调用_A__methodName()(试图调用__methodName()将会生成一个错误)

  • 在A类内部调用这个成员函数,你可以使用_A__methodName()__methodName()


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接