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

时间:2011-02-02 16:52:57

标签: python class metaprogramming magic-methods

>>> 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 个答案:

答案 0 :(得分:94)

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

要理解这一点,您必须阅读documentation of the descriptor protocol

简短版本:

  1. 对于课程A的实例,instance.__dict__提供对A.__dict__['__dict__']的访问权限,与vars(A)['__dict__']相同。
  2. 对于A类,A.__dict__(理论上)提供对type.__dict__['__dict__']的访问权限,与vars(type)['__dict__']相同。

  3. 长版:

    类和对象都通过属性运算符(通过类或元类的__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