Python:动态类生成:覆盖成员

时间:2009-11-24 18:48:36

标签: python inheritance name-mangling dynamic-class-creation

我有一个python类层次结构,我想在运行时扩展。此外,此层次结构中的每个类都有一个静态属性“dict”,我想在每个子类中覆盖它。简化它看起来像这样:

'dict'是受保护的(公共但具有前导下划线)成员

class A(object):
    _dict = {}

    @classmethod
    def getdict(cls):
        return cls._dict

    @classmethod
    def setval(cls, name, val):
        cls._dict[name] = val

    @classmethod
    def addchild(cls, name):
        return type(name, (cls, ), { '_dict' : {} })

B = A.addchild('B')
A.setval(1, 5)

print A.getdict()
# prints: {1: 5}
# like expected

print B.getdict()
# prints: {}
# like expected

这与预期一样有效。现在的问题是:如果我将属性声明为private,为什么它不再起作用:

现在与“dict”私有成员相同的事情

class C(object):
    __dict = {}

    @classmethod
    def getdict(cls):
        return cls.__dict

    @classmethod
    def setval(cls, name, val):
        cls.__dict[name] = val

    @classmethod
    def addchild(cls, name):
        return type(name, (cls, ), { '__dict' : {} })

D = C.addchild('D')
C.setval(1, 5)

print C.getdict()
# prints: {1: 5}
# like expected

print D.getdict()
# prints: {1: 5}
# why!?

突然DC的子类,在'dict'中与其超类具有相同的值!?

有人可以这么善良并向我解释,原因是什么?提前谢谢!

3 个答案:

答案 0 :(得分:3)

phild,如您所知,当您使用双下划线__为属性名称添加前缀时,python解释器会自动将(mangles)属性名称从__attribute更改为_CLS__attribute,其中CLS是班级名称。

然而,当你说

return type(name, (cls, ), { '__dict' : {} })

字典{ '__dict' : {} }中的键不会被破坏。 __dict保持不变。

因此,D最终同时使用D._C__dictD.__dict

(Pdb) dir(D)
['_C__dict', '__class__', '__delattr__', '__dict', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'addchild', 'getdict', 'setval']

D._C__dict指的是C的class属性。所以当你运行

C.setval(1, 5)

您正在更改D._C__dict以及C._C__dict。他们是同一个。

答案 1 :(得分:2)

这是chapter in documentation about "private" attributes。我对你的课程定义进行了评论,使其更清晰:

class C(object):
    __dict = {} # This creates C.__dict__['_C__dict']

    @classmethod
    def getdict(cls):
        return cls.__dict # Uses cls.__dict__['_C__dict'] 

    @classmethod
    def setval(cls, name, val):
        cls.__dict[name] = val # Uses cls.__dict__['_C__dict'] 

    @classmethod
    def addchild(cls, name):
        return type(name, (cls, ), { '__dict' : {} }) # Creates child.__dict__['__dict']

即。所有子项都有自己的__dict属性,但只使用了一个基类。

答案 2 :(得分:1)

“受保护”和“私有”的Java或C ++概念不适用。命名约定Python做了一点,但不是你想象的。

__name会进行一些名称修改,因为名称被遮挡而难以访问。

您的_dict__dict只是类级属性,只是所有类的实例共享。