为什么__dict__类是映射代理?


76

我不知道为什么一个类__dict__是一个mappingproxy,但是一个实例__dict__只是一个普通的dict

>>> class A:
...     pass

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

Answers:


79

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

在其他地方,Python是一种“成年人同意语言”,这意味着对象的指示由用户公开和可变。但是,对于类的类级属性和方法,如果可以保证键是字符串,则可以在类级简化和加快用于属性和方法查找的常见案例代码。尤其是,通过假定类dict键为字符串,可以简化和加快用于新型类的__mro__搜索逻辑。


28
出于好奇:mappingproxyclass.__dict__class.__setattr__设为只读,以便仅作为设置类属性的途径,而正是该方法强制执行了limit
马丁·彼得斯

2
这也与任何覆盖type.__setattr__(希望/可能是一个很小的集合)的人有关,因为您不能写__dict__;您必须使用super()
凯文(Kevin)

4
它还可以确保,如果为类提供了一种新的魔术方法,Python可以更新相关的C级插槽。如果您通过使用诸如之类的方法绕过此方法gc.get_referents(FooClass.__dict__)[0]['__eq__'] = eqmethod,则的实例FooClass可能实际上未eqmethod用于==比较。
user2357112支持Monica 2015年

我猜想有人想将派生类序列化为Json真是倒霉:/
基本的

@MartijnPieters需要说明的是,这种只读性质是为什么只能通过对类本身的访问来重新分配类属性的原因?为何尝试通过访问实例来重新分配类属性会导致新的实例属性与类属性同名?
soporific312

10

映射代理只是没有__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起。以下代码显示了dict类型:

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

3

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

很难找到这种类型的文档,但是vars方法的文档对此进行了很好的描述(尽管有一段时间没有对此进行记录了):

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

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


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

  • Python 2.7: type(A.__dict__)返回<type 'dict'>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'>上述类型。
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.