为什么类__dict__是mappingproxy?

Jos*_*uis 60 python class python-3.x python-internals

我想知道为什么一个类__dict__是一个类mappingproxy,但实例__dict__只是一个简单的例子dict

>>> class A:
...     pass

>>> a = A()
>>> type(a.__dict__)
<class 'dict'>
>>> type(A.__dict__)
<class 'mappingproxy'>
Run Code Online (Sandbox Code Playgroud)

Ray*_*ger 65

这有助于解释器确保类级属性和方法的键只能是字符串.

在其他地方,Python是一种"同意成人语言",这意味着用户可以公开和改变对象的序列.但是,对于类的类级别属性和方法,如果我们可以保证键是字符串,我们可以简化和加速类级别的属性和方法查找的常见案例代码.特别是,通过假设类dict键是字符串,简化了新样式类的__mro__搜索逻辑并加快了速度.

  • 而对于好奇:`mappingproxy`使`class .__ dict__`只读,所以只有`class .__ setattr__`仍然是设置类属性的途径,而且是[强制执行限制]的方法(https:/ /hg.python.org/cpython/file/ed694938c61a/Objects/object.c#l1132). (21认同)
  • 它还确保如果为类提供一个新的魔术方法,Python可以更新相关的C级插槽.如果你使用像`gc.get_referents(FooClass .__字典__)[0] [ '__ eq__'] = eqmethod`,FooClass`的`实例可能无法实际使用`eqmethod`为``==比较绕过这个. (4认同)
  • 这也适用于覆盖`type .__ setattr__`(希望/可能是一个非常小的集合)的任何人,因为你不能写入`__dict__`; 你必须使用`super()`. (2认同)

pro*_*sti 12

mappingproxy 只是一个没有__setattr__方法的字典。

您可以查看并参考此代码。

from types import MappingProxyType
d={'key': "value"}
m = MappingProxyType(d)
print(type(m)) # <class 'mappingproxy'>

m['key']='new' #TypeError: 'mappingproxy' object does not support item assignment
Run Code Online (Sandbox Code Playgroud)

mappingproxy 从 Python 3.3 开始。以下代码显示了 dict 类型:

class C:pass
ci=C()
print(type(C.__dict__)) #<class 'mappingproxy'>
print(type(ci.__dict__)) #<class 'dict'>
Run Code Online (Sandbox Code Playgroud)


fun*_*man 7

因为Python 3.3mappingproxy类型已改名为dictproxy。关于这个话题有一个有趣的讨论

找到这种类型的文档有点困难,但是vars方法的文档完美地描述了这一点(尽管有一段时间没有记录):

模块和实例等对象具有可更新的__dict__ 属性;但是,其他对象可能对其__dict__属性有写限制 (例如,类使用 types.MappingProxyType 来防止直接更新字典)。

如果您需要分配一个新的类属性,您可以使用setattr. 值得注意的mappingproxy是,JSON 不是可序列化的,请查看问题以了解原因。


这种类型的历史也很有趣:

  • Python 2.7: type(A.__dict__)返回<type 'dict'>as type(dict()),并且可以通过 分配新属性__dict__,例如A.__dict__['foo'] = 'bar'
  • Python 3.0 - 3.2: type(A.__dict__)返回<class 'dict_proxy'>,引入区别。尝试分配一个新的属性会给TypeError. 曾尝试将其添加dictproxy为公共内置类型。
  • Python 3.3:添加了上述<class 'mappingproxy'>类型。