Python类的__dict __.__ dict__属性是什么?


92
>>> 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__,当它包含的东西吗?


11
一个更合适的示例变量应该是ive。至少它将使这个A.__dict__['ive']问题成为更多问题;)我会发现自己的问题
Joakim 2015年

Answers:


110

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

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

简短版本:

  1. 对于类的一个实例A,接入instance.__dict__提供了一种由A.__dict__['__dict__']该相同vars(A)['__dict__']
  2. 对于A类,(理论上)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__ofinstance存储在__dict__实例中,因此可以直接通过描述符协议进行访问,并将其存储在实例的特殊字段中。

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

默认情况下,一个vars(cls)空类的a带有三个描述符-__dict__用于存储实例的属性(__weakref__weakref,在内部使用)和该类的文档字符串。如果定义,则前两个可能会消失__slots__。然后,您将没有__dict__and__weakref__属性,但是每个插槽只有一个class属性。这样,实例的属性将不会存储在字典中,并且将由类中的各个描述符提供对它们的访问。


最后,与之A.__dict__不同的A.__dict__['__dict__']是,因为该属性从未__dict__例外地在中查找过,所以对它的正确性对于实际上将要使用的任何其他属性都不是正确的。例如,与是同一件事。如果不存在这种不一致,则使用将不起作用,而您必须始终使用它。vars(A)A.__weakref__A.__dict__['__weakref__']A.__dict__vars(A)


6
感谢您的详细回答。尽管我不得不阅读几次,但自从我学习了Python的许多新细节以来,我觉得已经有一段时间了。
porgarmingduod'2

为什么__dict__对象的属性完全不能存储在对象的中__dict__
zumgruenenbaum

2
@zumgruenenbaum既然__dict__是为了存储所有实例的属性,以下形式的属性的访问obj.x最终抬头的对象__dict__,即obj.__dict__['x']。现在,如果__dict__未将其实现为描述符,则将导致无限递归,因为要进行访问,obj.__dict__您需要将其查找为obj.__dict__['__dict__']。描述符避免了这个问题。
a_guest

11

由于A.__dict__是存储A属性的字典,因此A.__dict__['__dict__']是对该属性的直接引用A.__dict__

A.__dict__包含对自身的(某种)引用。“种类”部分是为什么表达式A.__dict__返回adictproxy而不是normal的原因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'

9
A.__dict__['__dict__']不是对的引用A.__dict__。它实现__dict__了实例的属性。要自己尝试,请A.__dict__['__dict__'].__get__(A(), A)返回的属性A(),而A.__dict__['__dict__'].__get__(A, type)失败。
Rosh Oxymoron

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__


PEP 252说这__objclass__定义此属性的类,而不是那一类的属性。这使您的getattr示例不正确。一个更正确的getattr(A().__dict__['__dict__'].__objclass__, A.__dict__['__dict__'].__name__)
答案

1
@RoshOxymoronKeyError: '__dict__'与@AndrewClark的相反,您的表情提高了。
Maggyero

9

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

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

1
奇怪的is是,==在第二次比较中用if代替了,即在python 2.7.15+和3.6.8中都得到A.__dict__ is type.__dict__['__dict__'].__get__(A)了结果False
Arne Vogel
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.