如果你所说的“字典等价物”是指类和实例属性必须存储在映射中,那么你是对的。如果你认为 Python 数据模型像字典一样简单,那你就错了。
映射有一个特殊的属性__dict__:
object.__dict__
用于存储对象(可写)属性的字典或其他映射对象。
但是你必须区分类属性和实例属性:
>>> class A:
... class_attr = 5
... def __init__(self): self.instance_attr = 10
...
>>> A.__dict__
mappingproxy({'__module__': '__main__', 'class_attr': 5, '__init__': <function A.__init__ at ...>, '__dict__': <attribute '__dict__' of 'A' objects>, '__weakref__': <attribute '__weakref__' of 'A' objects>, '__doc__': None})
>>> a = A()
>>> a.__dict__
{'instance_attr': 10}
如您所见,class_attr 在 class A 的映射中,而instance_attr 在 instance a 的映射中。
你可以从实例上升到它的类:
>>> a.__class__.__dict__
mappingproxy({'__module__': '__main__', 'class_attr': 5, '__init__': <function A.__init__ at ...>, '__dict__': <attribute '__dict__' of 'A' objects>, '__weakref__': <attribute '__weakref__' of 'A' objects>, '__doc__': None})
但是Python可以解析来自实例的类属性调用,即使属性不在实例的映射中:
>>> a.class_attr
5
这是黑魔法!
让我们试试你的例子:
>>> class Base:
... x = 10
...
>>> class Derived(Base):
... y = 20
...
>>> Base.__dict__
mappingproxy({'__module__': '__main__', 'x': 10, '__dict__': <attribute '__dict__' of 'Base' objects>, '__weakref__': <attribute '__weakref__' of 'Base' objects>, '__doc__': None})
>>> Derived.__dict__
mappingproxy({'__module__': '__main__', 'y': 20, '__doc__': None})
如您所见,字典是正交的,因为 x 和 y 是类实例。因此,x 不在Derived 的映射中。让我们回答您提出的问题:Derived 等效字典是:
Derived: {"y": 10}
但是:
>>> Derived.x
10
更多的黑魔法!
让我们试试实例属性:
>>> class Base:
... def __init__(self):
... self.x = 10
...
>>> class Derived(Base):
... def __init__(self):
... self.y = 20
...
>>> b = Base()
>>> b.__dict__
{'x': 10}
>>> d = Derived()
>>> d.__dict__
{'y': 20}
但这一次,它不会像上面那样工作:
>>> d.x
Traceback (most recent call last):
...
AttributeError: 'Derived' object has no attribute 'x'
您需要通过显式调用其父初始化程序来修复Derived.__init__ 方法:
>>> class Derived(Base):
... def __init__(self):
... super(Derived, self).__init__()
... self.y = 20
...
你得到:
>>> d = Derived()
>>> d.__dict__
{'x': 10, 'y': 20}
>>> d.x
10
我希望您确信实例、类和字典是完全不同的,尽管它们共享映射的概念。 Python Data Model documentation 中解释了主要区别(即“黑魔法”),您可以在 SO:What is getattr() exactly and how do I use it? 上找到一些有用的问题/答案。
奖励:您在评论中写道
class A:
def __init__(self):
self.x = 10
a = A()
print(a.x)
可以写成:
def init(obj):
obj["x"] = 10
A = {"init": init}
a = dict(A)
a["init"](a)
print(a["x"])
但这是类和实例属性之间的混淆,因为init 不是实例a 的属性,而是类A 的属性。你应该写:
A = {"init": init}
a = {}
A["init"](a)
print(a["x"])
请注意,a = {} 行在 A.__init__ 方法中没有可见的等效项。当您编写self.x = 10 时,self 已经初始化。这是A.__new__ 方法的工作:
>>> class A:
... def __new__(cls):
... o = super().__new__(cls) # this is the equivalent of a = {}
... cls.last_created = o # store the object for test below
... return o
...
... def __init__(self):
... self.x = 10
...
>>> A.last_created
Traceback (most recent call last):
...
AttributeError: type object 'A' has no attribute 'last_created'
>>> a = A()
>>> a == A.last_created
True