【问题标题】:Why is a class __dict__ a mappingproxy?为什么类 __dict__ 是映射代理?
【发布时间】:2015-12-19 15:28:37
【问题描述】:

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

>>> class A:
...     pass

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

【问题讨论】:

    标签: python python-3.x class dictionary python-internals


    【解决方案1】:

    从 Python 3.3 开始,mappingproxy 类型为 renamed,来自 dictproxy。在这个话题上有一个有趣的discussion

    找到这种类型的文档有点困难,但是vars 方法的文档完美地描述了这一点(虽然它有一段时间是wasn't documented):

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

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


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

    • Python 2.7: type(A.__dict__)&lt;type 'dict'&gt; 返回为type(dict()),并且可以通过__dict__ 分配新属性,例如A.__dict__['foo'] = 'bar'.
    • Python 3.0 - 3.2: type(A.__dict__) 返回&lt;class 'dict_proxy'&gt;,区别介绍。尝试分配新属性会得到TypeError。有一个 attemptdictproxy 添加为公共内置类型。
    • Python 3.3:添加了上述&lt;class 'mappingproxy'&gt; 类型。

    【讨论】:

      【解决方案2】:

      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 开始。以下代码显示了dict类型:

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

      【讨论】:

        【解决方案3】:

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

        在其他地方,Python 是一种“同意成人语言”,这意味着对象的 dicts 由用户公开和可变。但是,在类的类级别属性和方法的情况下,如果我们可以保证键是字符串,我们可以简化和加速类级别的属性和方法查找的常见案例代码。特别是,通过假设类 dict 键是字符串,简化和加速了新型类的 __mro__ 搜索逻辑。

        【讨论】:

        • 对于好奇的人:mappingproxy 使class.__dict__ 只读,因此只有class.__setattr__ 仍然作为设置类属性的途径,而enforces the restriction 正是这种方法。
        • 这也适用于任何覆盖type.__setattr__(希望/可能是一个很小的集合)的人,因为你不能写信给__dict__;你必须使用super()
        • 它还确保如果你给一个类一个新的魔法方法,Python可以更新相关的C级槽。如果您通过使用 gc.get_referents(FooClass.__dict__)[0]['__eq__'] = eqmethod 之类的内容绕过此问题,FooClass 的实例实际上可能不会使用 eqmethod 进行 == 比较。
        • 我想任何想要将派生类序列化为 Json 的人都是不走运的:/
        • @MartijnPieters 澄清一下,这种只读性质是为什么只能通过类本身的访问来重新分配类属性的原因?为什么尝试通过实例访问重新分配类属性会导致与类属性同名的新实例属性?
        猜你喜欢
        • 2013-11-29
        • 2011-06-20
        • 1970-01-01
        • 2013-08-26
        • 1970-01-01
        • 2021-08-01
        • 2013-05-11
        • 2017-08-31
        相关资源
        最近更新 更多