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

por*_*uod 81 python metaprogramming class 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?
Run Code Online (Sandbox Code Playgroud)

如果我这样做A.something = 10,这就进入了A.__dict__.什么这个<attribute '__dict__' of 'A' objects>发现A.__dict__.__dict__,当它包含的东西吗?

Ros*_*ron 101

首先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__instance要被存储在__dict__该实例的,它是通过描述符访问协议,而不是直接,并且被存储在一个特殊字段中的实例.

类似的情况对于类来说也是如此,尽管它们__dict__是一个假装成字典的特殊代理对象(但可能不是内部的),并且不允许您更改它或用另一个替换它.此代理允许您访问特定于其中的类的属性,而不是在其中一个基础中定义.

默认情况下,一个vars(cls)空类的一个带有三个描述符 - __dict__用于存储实例的属性,这些属性由__weakref__内部使用weakref,以及类的docstring.如果你定义,前两个可能会消失__slots__.然后,你就不会__dict____weakref__属性,而是你必须为每个插槽一个类属性.然后,实例的属性将不存储在字典中,并且将由类中的相应描述符提供对它们的访问.


最后,A.__dict__不同的不一致A.__dict__['__dict__']是因为该属性__dict__是异常的,从不抬头vars(A),所以对于它来说实际上对于你使用的任何其他属性都是如此.例如,A.__weakref__与之相同A.__dict__['__weakref__'].如果不存在这种不一致,则使用A.__dict__不起作用,而您必须始终使用vars(A).

  • 谢谢你的详细解答.虽然我必须阅读几次,但我认为已经有一段时间了,因为我学到了很多关于Python的新细节. (6认同)
  • 为什么对象的 __dict__ 属性不能存储在对象的 __dict__ 中? (2认同)
  • @zumgruenenbaum 由于“__dict__”旨在存储所有实例属性,因此最终会在对象的“__dict__”上查找“obj.x”形式的属性访问,即“obj.__dict__['x']”。现在,如果“__dict__”没有作为描述符实现,这将导致无限递归,因为为了访问“obj.__dict__”,您需要将其查找为“obj.__dict__['__dict__']”。描述符避免了这个问题。 (2认同)

vz0*_*vz0 10

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

A.__dict__包含对自身的(种类)引用."kind-of"部分是表达式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'
Run Code Online (Sandbox Code Playgroud)

  • `A .__ dict __ ['__ dict __']`不是对'A .__ dict__`的引用.它实现了实例的`__dict__`属性.为了自己尝试这个,`A .__ dict __ ['__ dict __'] .__ get __(A(),A)`返回`A()`的属性,而`A .__ dict __ ['__ dict __'] .__ get __(A,类型)`失败. (6认同)

And*_*ark 9

让我们做一些探索!

>>> A.__dict__['__dict__']
<attribute '__dict__' of 'A' objects>
Run Code Online (Sandbox Code Playgroud)

我不知道那是什么?

>>> type(A.__dict__['__dict__'])
<type 'getset_descriptor'>
Run Code Online (Sandbox Code Playgroud)

getset_descriptor对象有什么属性?

>>> type(A.__dict__["__dict__"]).__dict__
<dictproxy object at 0xb7efc4ac>
Run Code Online (Sandbox Code Playgroud)

通过使的那一份dictproxy,我们可以发现一些有趣的特性,特别是__objclass____name__.

>>> A.__dict__['__dict__'].__objclass__, A.__dict__['__dict__'].__name__
(<class '__main__.A'>, '__dict__')
Run Code Online (Sandbox Code Playgroud)

所以__objclass__是一个参考A,并__name__仅仅是字符串'__dict__',也许是一个属性的名字吗?

>>> getattr(A.__dict__['__dict__'].__objclass__, A.__dict__['__dict__'].__name__) == A.__dict__
True
Run Code Online (Sandbox Code Playgroud)

我们有它! A.__dict__['__dict__']是一个可以参考的对象A.__dict__.


dam*_*ang 8

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

>>> 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
Run Code Online (Sandbox Code Playgroud)

从上面的例子看,类对象属性似乎是由它们的类存储的,类的属性是由它们的类存储的,它们是元类.这也通过以下方式验证:

>>> a.__dict__ == A.__getattribute__(a, '__dict__')
True
>>> A.__dict__ == type.__getattribute__(A, '__dict__')
True
Run Code Online (Sandbox Code Playgroud)

  • 奇怪的是,如果在第二次比较中用“is”替换“==”,即“A.__dict__ is type.__dict__['__dict__'].__get__(A)”,则在 python 2.7 中结果都是“False” .15+ 和 3.6.8。 (3认同)
  • @ArneVogel这是因为表达式 `A.__dict__` (或 `type.__dict__['__dict__'].__get__(A)`)计算结果为 *new* [`types.MappingProxyType`](https://docs. python.org/3/library/types.html#types.MappingProxyType) 实例:`A.__dict__ 不是 A.__dict__`。(有关此类型历史的更多信息[此处](/sf/answers/4146333411/)。)与表达式“a.__dict__”(或“A.__dict__['__dict__']”相反。 __get__(a)`) 计算结果为相同的 `dict` 实例:`a.__dict__ is a.__dict__`。 (2认同)