>>> class A(object): pass
...
>>> A.__dict__
<dictproxy object at 0x173ef30>
>>> A.__dict__.__dict__
Traceback (most recent call last):
File "<string>", line 1, in <fragment>
AttributeError: 'dictproxy' object has no attribute '__dict__'
>>> A.__dict__.copy()
{'__dict__': <attribute '__dict__' of 'A' objects> ... }
>>> A.__dict__['__dict__']
<attribute '__dict__' of 'A' objects> # What is this object?
如果我A.something = 10
,则进入A.__dict__
。 是 <attribute '__dict__' of 'A' objects>
中找到的A.__dict__.__dict__
,以及何时包含内容?
答案 0 :(得分:94)
首先A.__dict__.__dict__
与A.__dict__['__dict__']
不同,前者不存在。后者是类的实例所具有的__dict__
属性。它是一个描述符对象,它返回特定实例的内部属性字典。简而言之,对象的__dict__
属性不能存储在对象的__dict__
中,因此可以通过类中定义的描述符进行访问。
要理解这一点,您必须阅读documentation of the descriptor protocol。
简短版本:
A
的实例,instance.__dict__
提供对A.__dict__['__dict__']
的访问权限,与vars(A)['__dict__']
相同。A.__dict__
(理论上)提供对type.__dict__['__dict__']
的访问权限,与vars(type)['__dict__']
相同。长版:
类和对象都通过属性运算符(通过类或元类的__getattribute__
实现)和__dict__
使用的vars(ob)
属性/协议提供对属性的访问。
对于普通对象,__dict__
对象创建一个单独的dict
对象,用于存储属性,__getattribute__
首先尝试访问它并从那里获取属性(在尝试之前)在调用__getattr__
之前,使用描述符协议查找类中的属性。该类的__dict__
描述符实现了对该字典的访问。
x.name
相当于按顺序尝试:x.__dict__['name']
,type(x).name.__get__(x, type(x))
,type(x).name
x.__dict__
做同样的事情,但出于显而易见的原因跳过第一个由于__dict__
instance
不可能存储在实例的__dict__
中,而是直接通过描述符协议访问它,并存储在特殊字段中实例。
对于类来说类似的情况也是如此,尽管它们的__dict__
是一个特殊的代理对象,它假装是一个字典(但可能不是内部的),并且不允许你更改或替换它另一个。除其他所有内容外,此代理允许您访问特定于其的类的属性,而不是在其基础中定义。
默认情况下,空类的vars(cls)
带有三个描述符 - __dict__
用于存储__weakref__
内部使用的实例属性weakref
,以及该类的文档字符串。如果您定义__slots__
,前两个可能会消失。那么你就没有__dict__
和__weakref__
属性,而是每个插槽都有一个类属性。然后,实例的属性将不会存储在字典中,并且将由类中的相应描述符提供对它们的访问。
最后,A.__dict__
与A.__dict__['__dict__']
不同的不一致是因为__dict__
属性,例外情况,永远在{{1}中查找对于它来说,对于它实际上你使用的任何其他属性都是如此。例如,vars(A)
与A.__weakref__
相同。如果不存在此不一致,则使用A.__dict__['__weakref__']
将无效,您必须始终使用A.__dict__
。
答案 1 :(得分:8)
由于A.__dict__
是存储A
属性的字典,A.__dict__['__dict__']
是对同一A.__dict__
属性的直接引用。
A.__dict__
包含对自身的(种类)引用。 “亲切”部分是表达式A.__dict__
返回dictproxy
而不是正常dict
的原因。
>>> class B(object):
... "Documentation of B class"
... pass
...
>>> B.__doc__
'Documentation of B class'
>>> B.__dict__
<dictproxy object at 0x00B83590>
>>> B.__dict__['__doc__']
'Documentation of B class'
答案 2 :(得分:7)
让我们做一些探索!
>>> A.__dict__['__dict__']
<attribute '__dict__' of 'A' objects>
我想知道那是什么?
>>> type(A.__dict__['__dict__'])
<type 'getset_descriptor'>
getset_descriptor
对象有哪些属性?
>>> type(A.__dict__["__dict__"]).__dict__
<dictproxy object at 0xb7efc4ac>
通过制作dictproxy
的副本,我们可以找到一些有趣的属性,特别是__objclass__
和__name__
。
>>> A.__dict__['__dict__'].__objclass__, A.__dict__['__dict__'].__name__
(<class '__main__.A'>, '__dict__')
所以__objclass__
是对A
的引用,而__name__
只是字符串'__dict__'
,属性的名称可能是?
>>> getattr(A.__dict__['__dict__'].__objclass__, A.__dict__['__dict__'].__name__) == A.__dict__
True
我们有它! A.__dict__['__dict__']
是一个可以引用A.__dict__
的对象。
答案 3 :(得分:6)
您可以尝试以下简单示例来了解更多内容:
carousel-inner
从上面的例子看,类对象属性似乎是由它们的类存储的,类的属性是由它们的类存储的,它们是元类。这也通过以下方式验证:
>>> class A(object): pass
...
>>> a = A()
>>> type(A)
<type 'type'>
>>> type(a)
<class '__main__.A'>
>>> type(a.__dict__)
<type 'dict'>
>>> type(A.__dict__)
<type 'dictproxy'>
>>> type(type.__dict__)
<type 'dictproxy'>
>>> type(A.__dict__['__dict__'])
<type 'getset_descriptor'>
>>> type(type.__dict__['__dict__'])
<type 'getset_descriptor'>
>>> a.__dict__ == A.__dict__['__dict__'].__get__(a)
True
>>> A.__dict__ == type.__dict__['__dict__'].__get__(A)
True
>>> a.__dict__ == type.__dict__['__dict__'].__get__(A)['__dict__'].__get__(a)
True