为什么 class __dict__ 是映射代理?

Why is a class __dict__ a mappingproxy?

我想知道为什么 class __dict__mappingproxy,而实例 __dict__ 只是普通的 dict

>>> class A:
...     pass

>>> a = A()
>>> type(a.__dict__)
<class 'dict'>
>>> type(A.__dict__)
<class 'mappingproxy'>

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

在其他地方,Python 是一个 "consenting adults language",这意味着对象的字典由用户公开和可变。但是,对于classes的class级属性和方法,如果能保证key是字符串,就可以简化和加速属性和方法查找的common case代码在 class 级。特别是,__mro__ 新式 classes 的搜索逻辑通过假设 class dict 键是字符串得到简化和加速。

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

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

class C:pass
ci=C()
print(type(C.__dict__)) #<class 'mappingproxy'>
print(type(ci.__dict__)) #<class 'dict'>

自 Python 3.3 mappingproxy 类型 renamed from dictproxy. There was an interesting discussion 关于此主题。

这个类型的文档有点难找,不过vars method describes this perfectly (though it wasn't documented找了一会儿的文档):

Objects such as modules and instances have an updateable __dict__ attribute; however, other objects may have write restrictions on their __dict__ attributes (for example, classes use a types.MappingProxyType to prevent direct dictionary updates).

如果您需要assign a new class attribute you could use setattr. It worth to note that mappingproxy is not JSON serializable, check out the issue了解原因。


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

  • Python 2.7: type(A.__dict__) returns <type 'dict'> as type(dict()),并且可以分配新的通过 __dict__ 的属性,例如A.__dict__['foo'] = 'bar'.
  • Python 3.0 - 3.2: type(A.__dict__) returns <class 'dict_proxy'>,区别介绍。尝试分配一个新属性会得到 TypeError。有一个 attempt 添加 dictproxy 作为 public 内置类型。
  • Python 3.3: 添加上述 <class 'mappingproxy'> 类型。