102
>>> 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__,它什么时候包含一些东西?

4

3 回答 3

112

首先A.__dict__.__dict__不同于A.__dict__['__dict__']. 前者不存在,后者是__dict__类的实例所具有的属性。它是一个数据描述符对象,它返回特定实例的内部属性字典。简而言之,__dict__对象的属性不能存储在对象的__dict__中,因此可以通过类中定义的描述符来访问它。

要理解这一点,您必须阅读描述符协议的文档

简短版本:

  1. a对于类的实例A,访问a.__dict__A.__dict__['__dict__']which 提供,与 相同vars(A)['__dict__']
  2. 对于一个类A,访问A.__dict__type.__dict__['__dict__'](理论上)提供,这与vars(type)['__dict__'].

长版:

类和对象都通过属性运算符(通过类或元类的 实现)__getattribute__和.__dict__vars(ob)

对于普通对象,该__dict__对象创建一个单独的dict对象,该对象存储属性,并__getattribute__首先尝试访问它并从那里获取属性(在尝试使用描述符协议在类中查找属性之前,在调用之前__getattr__)。类上的__dict__描述符实现了对这个字典的访问。

  • a.name相当于按顺序尝试:(type(a).__dict__['name'].__get__(a, type(a))仅当type(a).__dict__['name']数据描述符时), a.__dict__['name'], type(a).__dict__['name'].__get__(a, type(a)), type(a).__dict__['name'].
  • a.__dict__做同样的事情,但出于明显的原因跳过了第二步。

由于__dict__实例本身不可能存储,而是直接通过描述符协议访问,并存储在实例的特殊字段中。

类也有类似的情况,尽管它们__dict__是一个伪装成字典的特殊代理对象(但可能不在内部),并且不允许您更改它或将其替换为另一个。除其他外,此代理允许您访问特定于它的类的属性,而不是在其基础之一中定义的属性。

默认情况下,vars(cls)空类的 a 带有三个描述符:__dict__用于存储实例的属性,__weakref__由 内部使用weakref,以及__doc__类的文档字符串。如果您定义,前两个可能会消失__slots__。那么你就没有__dict____weakref__属性了,而是每个插槽都有一个类属性。实例的属性将不会存储在字典中,并且对它们的访问将由类中的相应描述符提供。


A.__dict__最后,与不同的不一致A.__dict__['__dict__']是因为该属性__dict__在例外情况下从未在 中查找过vars(A),因此对于您要使用的任何其他属性来说,它的正确性并不正确。例如,和A.__weakref__是一样的A.__dict__['__weakref__']。如果不存在这种不一致,则 usingA.__dict__将不起作用,您必须始终使用 use vars(A)

于 2011-02-02T17:26:02.523 回答
11

您可以尝试以下简单示例来了解更多信息:

>>> 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

从上面的例子可以看出,实例属性是由它们的类来存储的,而类属性是由它们的元类来存储的。这也通过以下方式验证:

>>> a.__dict__ == A.__getattribute__(a, '__dict__')
True
>>> A.__dict__ == type.__getattribute__(A, '__dict__')
True
于 2016-06-06T23:52:27.873 回答
10

让我们做一些探索!

>>> 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__

于 2011-02-02T17:38:05.433 回答