Python 类的 __dict__.__dict__ 属性是什么? [英] What is the __dict__.__dict__ attribute of a Python class?

查看:34
本文介绍了Python 类的 __dict__.__dict__ 属性是什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

<预><代码>>>>A类(对象):通过...>>>A.__dict__<0x173ef30 处的 dictproxy 对象>>>>A.__dict__.__dict__回溯(最近一次调用最后一次):文件<string>",第 1 行,在 <fragment>AttributeError: 'dictproxy' 对象没有属性 '__dict__'>>>A.__dict__.copy(){'__dict__':<'A' 对象的属性 '__dict__'>... }>>>A.__dict__['__dict__']<A"对象的__dict__"属性># 这个对象是什么?

如果我执行 A.something = 10,这会进入 A.__dict__.是什么这个A.__dict__.__dict__中找到,它什么时候包含一些东西?

解决方案

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

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

简短版本:

  1. 对于类A的实例a,对a.__dict__的访问由A.__dict__['__dict__]提供']vars(A)['__dict__'] 相同.
  2. 对于A类,type.__dict__['__dict__'](理论上)提供了对A.__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']data 描述符时), a.__dict__['name'], <代码>type(a).__dict__['name'].__get__(a, type(a)), type(a).__dict__['name'].
  • a.__dict__ 执行相同的操作,但出于显而易见的原因跳过了第二步.

由于实例的__dict__无法存储在自身中,因此直接通过描述符协议访问,并存储在实例的特殊字段中.

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

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


最后,A.__dict__A.__dict__['__dict__'] 的不一致是因为属性 __dict__ 是,作为例外,从不vars(A) 中查找,所以它的真值对于您实际使用的任何其他属性都不是真值.例如,A.__weakref__A.__dict__['__weakref__'] 是一回事.如果这种不一致不存在,则使用 A.__dict__ 将不起作用,您必须始终使用 vars(A) 代替.

>>> 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__. What is this <attribute '__dict__' of 'A' objects> found in A.__dict__.__dict__, and when does it contain something?

解决方案

First of all A.__dict__.__dict__ is different from A.__dict__['__dict__']. The former doesn't exist and the latter is the __dict__ attribute that the instances of the class would have. 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.

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__'].
  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__'].


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

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__). The __dict__ descriptor on the class implements the access to this dictionary.

  • 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.__dict__ does the same but skips the second step for obvious reasons.

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.

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. 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. The first two might be gone if you define __slots__. Then you wouldn't have __dict__ and __weakref__ attributes, but instead you'd have a single class attribute for each slot. 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. For example, A.__weakref__ is the same thing as 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.

这篇关于Python 类的 __dict__.__dict__ 属性是什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆