【问题描述】:

我了解 Python 不明确支持类中的私有变量。但是,请考虑以下程序:

class AClass(object):
    def __init__(self, x):
        self.__x = x



class BClass(object):
    def __init__(self, x):
        self.__x = x
# _____________________________________________________________________________

aClass = AClass(10)
bClass = BClass(10)

aClass.__x = 15
print (aClass.__x)


##bClass.__x = 20
print (bClass.__x)

上面的程序,会产生如下错误: AttributeError: 'BClass' 对象没有属性 '__x'

但是,如果倒数第二行代码没有注释,它会执行而不会出错。

如果有人可以澄清似乎不一致的地方,并且如果有 PEP 可以解释这种行为,我将不胜感激。

最好的问候。

BD

【问题讨论】:

    标签: variables private


    【解答1】:

    这是因为以 dunder 开头的变量名称被修改以“保护”它们。如果你检查 bClass 的字典,你会看到:

    >>> print(bClass.__dict__)
    {'_BClass__x': 10, '__x': 20}
    

    _BClass__x(我称它为对象变量)是由对象本身创建的,因此它的名字是乱七八糟的。 __x 是在类(a)外部 创建的,这就是为什么它有一个未损坏的名称,因此您可以访问它只需 __x.

    要访问这两种类型的对象变量,您可以使用:

    print (aClass._AClass__x)
    print (bClass._BClass__x)
    

    但我不确定这有多可靠。我确定这是你可能不应该做的事情,因为它破坏了封装:-)

    事实上,虽然我说重整是由对象完成的,但我想确保您理解它不是在实例化对象时完成的。实际的修改发生在代码编译时,如果你反汇编你可以看到:

    >>> import dis
    >>> dis.dis(AClass)
    Disassembly of __init__:
      3           0 LOAD_FAST                1 (x)
                  2 LOAD_FAST                0 (self)
                  4 STORE_ATTR               0 (_AClass__x)
                  6 LOAD_CONST               0 (None)
                  8 RETURN_VALUE
    

    STORE_ATTR 字节码实际上知道使用错误名称。


    (a) 它与对象变量非常不同,当您稍后尝试使用 __x 在成员函数中,并发现它没有被您的外部代码更改:-)

    【问题讨论】:

    • 我确实知道我已经使用``` print (dir(bClass))``进行了检查。我无法理解的是访问的不一致:写访问是无条件允许的,而读访问在执行写入后成为可能。
    • @user816270:读取仅在写入后有效,因为在类之外,__x 在您创建它之前实际上并不存在 (只有 ___x 存在)。在执行 varname = "something" 之前尝试 print(varname) 没有什么不同。