简体   繁体   English

Python 类的 __dict__.__dict__ 属性是什么?

[英]What is the __dict__.__dict__ attribute of a Python class?

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

If I do A.something = 10 , this goes into A.__dict__ .如果我做A.something = 10 ,这会进入A.__dict__ What is this <attribute '__dict__' of 'A' objects> found in A.__dict__.__dict__ , and when does it contain something?什么<attribute '__dict__' of 'A' objects>中发现A.__dict__.__dict__ ,当它包含的东西吗?

First of all A.__dict__.__dict__ is different from A.__dict__['__dict__'] .首先, A.__dict__.__dict__不同于A.__dict__['__dict__'] The former doesn't exist and the latter is the __dict__ attribute that the instances of the class would have.前者不存在,后者是类的实例将具有的__dict__属性。 It's a data descriptor object that returns the internal dictionary of attributes for the specific instance.它是一个数据描述符对象,它返回特定实例的内部属性字典。 In short, the __dict__ attribute of an object can't be stored in object's __dict__ , so it's accessed through a descriptor defined in the class.简而言之,对象的__dict__属性不能存储在对象的__dict__ ,因此它是通过类中定义的描述符访问的。

To understand this, you'd have to read the documentation of the descriptor protocol .要理解这一点,您必须阅读描述符协议文档

The short version:简短版本:

  1. For an instance a of a class A , access to a.__dict__ is provided by A.__dict__['__dict__'] which is the same as vars(A)['__dict__'] .对于类A的实例a ,对a.__dict__访问由A.__dict__['__dict__'] ,这与vars(A)['__dict__']
  2. For a class A , access to A.__dict__ is provided by type.__dict__['__dict__'] (in theory) which is the same as vars(type)['__dict__'] .对于类A ,访问A.__dict__type.__dict__['__dict__'] (理论上)提供,这与vars(type)['__dict__']

The long version:长版:

Both classes and objects provide access to attributes both through the attribute operator (implemented via the class or metaclass's __getattribute__ ), and the __dict__ attribute/protocol which is used by vars(ob) .类和对象都通过属性运算符(通过类或元类的__getattribute__ )和vars(ob)使用的__dict__属性/协议提供对属性的访问。

For normal objects, the __dict__ object creates a separate dict object, which stores the attributes, and __getattribute__ first tries to access it and get the attributes from there (before attempting to look for the attribute in the class by utilizing the descriptor protocol, and before calling __getattr__ ).对于普通对象, __dict__对象创建一个单独的dict对象,用于存储属性,而__getattribute__首先尝试访问它并从那里获取属性(在尝试使用描述符协议在类中查找属性之前,以及之前调用__getattr__ )。 The __dict__ descriptor on the class implements the access to this dictionary.类上的__dict__描述符实现了对这个字典的访问。

  • a.name is equivalent to trying those in order: type(a).__dict__['name'].__get__(a, type(a)) (only if type(a).__dict__['name'] is a data descriptor), a.__dict__['name'] , type(a).__dict__['name'].__get__(a, type(a)) , type(a).__dict__['name'] . 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__ does the same but skips the second step for obvious reasons. a.__dict__做同样的a.__dict__ ,但由于显而易见的原因跳过了第二步。

As it's impossible for the __dict__ of an instance to be stored in itself, it's accessed through the descriptor protocol directly instead and is stored in a special field in the instance.由于实例的__dict__不可能存储在自身中,因此直接通过描述符协议访问它并存储在实例的特殊字段中。

A similar scenario is true for classes, although their __dict__ is a special proxy object that pretends to be a dictionary (but might not be internally), and doesn't allow you to change it or replace it with another one.类也有类似的情况,尽管它们的__dict__是一个特殊的代理对象,它伪装成一个字典(但可能不是内部的),并且不允许您更改它或用另一个替换它。 This proxy allows you, among all else, to access the attributes of a class that are specific to it, and not defined in one of its bases.除其他外,此代理允许您访问特定于它的类的属性,而不是在其基类之一中定义。

By default, a vars(cls) of an empty class carries three descriptors: __dict__ for storing the attributes of the instances, __weakref__ which is used internally by weakref , and __doc__ the docstring of the class.缺省情况下, vars(cls)一个空类的承载三个描述符: __dict__用于存储实例的属性, __weakref__其通过内部使用weakref ,和__doc__类的文档字符串。 The first two might be gone if you define __slots__ .如果您定义__slots__ ,前两个可能会消失。 Then you wouldn't have __dict__ and __weakref__ attributes, but instead you'd have a single class attribute for each slot.那么你不会有__dict____weakref__属性,而是每个插槽都有一个类属性。 The attributes of the instance then wouldn't be stored in a dictionary, and access to them will be provided by the respective descriptors in the class.实例的属性不会存储在字典中,并且对它们的访问将由类中的相应描述符提供。


And lastly, the inconsistency that A.__dict__ is different from A.__dict__['__dict__'] is because the attribute __dict__ is, by exception, never looked up in vars(A) , so what is true for it isn't true for practically any other attribute you'd use.最后, A.__dict__A.__dict__['__dict__']的不一致是因为属性__dict__例外地从未vars(A)查找过,所以对它来说是真的几乎任何你会使用的其他属性。 For example, A.__weakref__ is the same thing as A.__dict__['__weakref__'] .例如, A.__weakref__A.__dict__['__weakref__'] If this inconsistency didn't exist, using A.__dict__ would not work, and you'd have to always use vars(A) instead.如果这种不一致不存在,则使用A.__dict__将不起作用,您必须始终使用vars(A)代替。

You can try the following simple example to understand more of this:您可以尝试以下简单示例来了解更多信息:

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

From the above example, it seems that instance attributes are stored by their class, and class attributes are stored by their metaclass.从上面的例子来看,实例属性是由它们的类存储的,而类属性是由它们的元类存储的。 This is also validated by:这也得到了验证:

>>> a.__dict__ == A.__getattribute__(a, '__dict__')
True
>>> A.__dict__ == type.__getattribute__(A, '__dict__')
True

Since A.__dict__ is a dictionary storing A attributes, A.__dict__['__dict__'] is the direct reference to that same A.__dict__ attribute. 由于A.__dict__是存储A属性的字典,因此A.__dict__['__dict__']是对该同一A.__dict__属性的直接引用。

A.__dict__ contains a (kind-of) reference to itself. A.__dict__包含对自身的(一种)引用。 The "kind-of" part is why the expression A.__dict__ returns a dictproxy instead of a normal 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'

Lets do some exploring!让我们做一些探索!

>>> A.__dict__['__dict__']
<attribute '__dict__' of 'A' objects>

I wonder what that is?我想知道那是什么?

>>> type(A.__dict__['__dict__'])
<type 'getset_descriptor'>

What attributes does a getset_descriptor object have? getset_descriptor对象有哪些属性?

>>> type(A.__dict__["__dict__"]).__dict__
<dictproxy object at 0xb7efc4ac>

By making a copy of that dictproxy we can find some interesting attributes, specifically __objclass__ and __name__ .通过复制该dictproxy我们可以找到一些有趣的属性,特别是__objclass____name__

>>> A.__dict__['__dict__'].__objclass__, A.__dict__['__dict__'].__name__
(<class '__main__.A'>, '__dict__')

So __objclass__ is a reference to A and __name__ is just the string '__dict__' , name of an attribute perhaps?所以__objclass__是对A的引用,而__name__只是字符串'__dict__' ,也许是一个属性的名称?

>>> getattr(A.__dict__['__dict__'].__objclass__, A.__dict__['__dict__'].__name__) == A.__dict__
True

There we have it!我们有! A.__dict__['__dict__'] is an object that can refer back to A.__dict__ . A.__dict__['__dict__']是一个可以引用回A.__dict__

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM