【问题标题】:Odd behaviour using a custom dict class as the __dict__ attribute of Python classes使用自定义 dict 类作为 Python 类的 __dict__ 属性的奇怪行为
【发布时间】:2011-03-03 16:08:56
【问题描述】:

我有一个从字典继承的类,以便添加一些自定义行为——在这种情况下,它将每个键和值传递给一个函数进行验证。在下面的示例中,“验证”只是打印一条消息。

对字典的分配按预期工作,每当将项目添加到字典时都会打印消息。但是,当我尝试将自定义字典类型用作类的 __dict__ 属性时,属性分配(又将键/值放入我的自定义字典类)以某种方式设法将值插入字典,同时完全绕过 __setitem__ (以及我定义的其他可能添加键的方法)。

自定义词典:

from collections import MutableMapping
class ValidatedDict(dict):
    """A dictionary that passes each value it ends up storing through
    a given validator function.
    """
    def __init__(self, validator, *args, **kwargs):
        self.__validator = validator
        self.update(*args, **kwargs)
    def __setitem__(self, key, value):
        self.__validator(value)
        self.__validator(key)
        dict.__setitem__(self, key, value)
    def copy(self): pass # snipped
    def fromkeys(validator, seq, v = None): pass # snipped
    setdefault = MutableMapping.setdefault
    update = MutableMapping.update

def Validator(i): print "Validating:", i

将它用作类的__dict__ 属性会产生我不理解的行为。

>>> d = ValidatedDict(Validator)
>>> d["key"] = "value"
Validating: value
Validating: key
>>> class Foo(object): pass
... 
>>> foo = Foo()
>>> foo.__dict__ = ValidatedDict(Validator)
>>> type(foo.__dict__)
<class '__main__.ValidatedDict'>
>>> foo.bar = 100 # Yields no message!
>>> foo.__dict__['odd'] = 99
Validating: 99
Validating: odd
>>> foo.__dict__
{'odd': 99, 'bar': 100}

有人可以解释为什么它的行为不符合我的预期吗?它可以或不能按照我尝试的方式工作吗?

【问题讨论】:

  • 我不认为你应该替换__dict__,除非你真的必须替换__new__。您在此处尝试执行的操作应使用自定义 __setattribute__ 方法完成。
  • 说实话,我并不是真的“试图做某事”。我正在编写验证 dict 类,只是为了好玩想尝试将其作为 __dict__ 实例。我提出问题的原因是我真的很想了解它为什么会这样,以及是否有可能绕过它(以及如何绕过它)。

标签: python attributes metaprogramming magic-methods


【解决方案1】:

这是一种优化。为了支持__dict__ 上的元方法,每个实例分配都需要检查元方法的存在。这是一项基本操作——每个属性查找和分配——因此检查这点所需的额外几个分支将成为整个语言的开销,因为obj.__getattr__obj.__setattr__ 或多或少是多余的。

【讨论】:

  • 那么,答案显然是__dict__ 实例上的元方法绝对不起作用?我可以根据您知识渊博的回答(和您的声誉)假设您基本上完全知道您在说什么,并且我可以接受这个回答,因为关于这个问题的所有内容都可以说?
  • PyObject_GenericSetAttr的Objects/object.c中硬编码:如果对象有__dict__,它会调用PyDict_SetItem,它直接设置字典值而不调用任何元方法。我认为这也是为什么你不能将__dict__ 设置为不是dict 的子类的原因;例如,如果你尝试x.__dict__ = [],它会抛出异常。
  • 我将其称为 Python 文档没有明确提及这一点的错误。 class 文档(不是 class instance)专门说 C.x is translated to C.__dict__["x"],人们很容易看到这一点并假设类实例也是如此,所以这可以更清楚。 docs.python.org/reference/datamodel.html
猜你喜欢
  • 1970-01-01
  • 2012-10-21
  • 2017-04-10
  • 2015-02-24
  • 2014-12-28
  • 2015-08-09
  • 1970-01-01
  • 2016-07-14
  • 2013-10-09
相关资源
最近更新 更多