Python:动态类生成:覆盖成员
我有一个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
这就像预期的那样工作。现在的问题是:如果我把这个属性声明为私有的,为什么它就不再工作了呢?
现在同样的事情,'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!?
突然间,D
这个类(它是C
的子类)在'dict'中有和它的父类一样的值!?
有没有人能好心地解释一下,这是什么原因呢?谢谢!
3 个回答
在Java或C++中,“protected”和“private”的概念并不适用。在Python中有一些命名规则,但和你想的并不完全一样。
使用__name
会进行一些名称的混淆,这样就很难直接访问,因为这个名字被隐藏了。
你的_dict
和__dict
其实只是类级别的属性,所有这个类的实例都会共享这些属性。
这里有一段关于“私有”属性的文档章节,您可以查看一下:文档链接。我在您的类定义上做了一些注释,以便让它更清晰:
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
属性,但只有一个来自基类的属性会被使用。
phild,正如你所知道的,当你在属性名前加上两个下划线__
时,Python解释器会自动把这个属性名改成一个特殊的形式,比如从__attribute
变成_CLS__attribute
,其中CLS是类的名字。
但是,当你写
return type(name, (cls, ), { '__dict' : {} })
时,字典{ '__dict' : {} }
里的键名不会被改名。__dict
保持不变。
所以,最后D类里会有D._C__dict
和D.__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类的属性。所以当你执行
C.setval(1, 5)
时,你实际上是在改变D._C__dict
和C._C__dict
,它们是同一个东西。