如何访问添加到 class 的方法中的“__”(双下划线)变量

How to access "__" (double underscore) variables in methods added to a class

背景

我希望使用元 class 以添加基于原始 class 的辅助方法。如果我想添加的方法使用 self.__attributeName,我会得到一个 AttributeError(因为名称修改),但对于现有的相同方法,这不是问题。

代码示例

这是一个简化的例子

# Function to be added as a method of Test
def newfunction2(self):
    """Function identical to newfunction"""
    print self.mouse
    print self._dog
    print self.__cat

class MetaTest(type):
    """Metaclass to process the original class and
    add new methods based on the original class
    """
    def __new__(meta, name, base, dct):
        newclass = super(MetaTest, meta).__new__(
                meta, name, base, dct
                )

        # Condition for adding newfunction2
        if "newfunction" in dct:
            print "Found newfunction!"
            print "Add newfunction2!"
            setattr(newclass, "newfunction2", newfunction2)

        return newclass

# Class to be modified by MetaTest
class Test(object):
    __metaclass__ = MetaTest

    def __init__(self):
        self.__cat = "cat"
        self._dog = "dog"
        self.mouse = "mouse"

    def newfunction(self):
        """Function identical to newfunction2"""
        print self.mouse
        print self._dog
        print self.__cat

T = Test()
T.newfunction()
T.newfunction2() # AttributeError: 'Test' object has no attribute '__cat'

问题

有没有办法添加可以使用 self.__catnewfunction2

(没有将 self.__cat 重命名为 self._cat。)

也许还有一些更基本的东西,既然 newfunction2 现在是 Test 的一部分,为什么 self.__cat 不以同样的方式对待这两种情况?

您可以使用 <Test instance>._Test__catTest class 访问 __cat 属性。 (其中 <Test instance> 替换为 selfTest class 的任何其他实例)

Python doc

中了解更多信息

回答你的两个问题:

  1. 由于名称修改规则,当您需要将其从 newfunction2 调用为 self._Test__cat 时,您需要更改 self.__cat

  1. Python documentation:

This mangling is done without regard to the syntactic position of the identifier, as long as it occurs within the definition of a class.

让我为您打断一下,它是说,当您的解释器遇到名称损坏的名称时,它在何处阅读并不重要。 只有 如果 它出现在 class 的定义中,名称才会被损坏,而在您的情况下,它不是。因为它不是直接 "under" 一个 class 定义。因此,当它读取 self.__cat 时,它将保持在 self.__cat 而不是 ,因为它没有在Test class.

编译 class 中的方法时会发生名称重整。 __foo 之类的属性名称被转换为 _ClassName__foo,其中 ClassName 是定义方法的 class 的名称。请注意,您可以对以下属性使用名称重整其他对象!

在您的代码中,newfunction2 中的名称修改不起作用,因为编译函数时,它不是 class 的一部分。因此 __cat 的查找不会像在 Test.__init__ 中那样变成 __Test_cat。如果需要,您可以明确查找属性名称的损坏版本,但听起来您希望 newfunction2 是通用的,并且能够添加到多个 classes。不幸的是,这不适用于名称修改。

确实,防止未在 class 中定义的代码访问您的属性是使用名称修改的全部原因。通常只有当你正在编写代理或混合类型并且你不希望你的内部使用属性与你正在代理或混合的 class 的属性发生冲突时才值得烦恼(你不会提前知道)。

class B:
    def __init__(self):
        self.__private = 0 
    def __private_method(self):
        '''A private method via inheritance'''
        return ('{!r}'.format(self))
    def internal_method(self):
        return ('{!s}'.format(self))

class C(B):
    def __init__(self):
        super().__init__()
        self.__private = 1
    def __private_method(self):
        return 'Am from class C'

c = C()
print(c.__dict__)
b = B()
print(b.__dict__)
print(b._B__private)
print(c._C__private_method())