考虑此代码:

class Foo1(dict):
    def __getattr__(self, key): return self[key]
    def __setattr__(self, key, value): self[key] = value

class Foo2(dict):
    __getattr__ = dict.__getitem__
    __setattr__ = dict.__setitem__

o1 = Foo1()
o1.x = 42
print(o1, o1.x)

o2 = Foo2()
o2.x = 42
print(o2, o2.x)

我期待同样的产出。但是,使用cpython 2.5、2.6(类似于3.2),我得到:
({'x': 42}, 42)
({}, 42)

通过pypy 1.5.0,我得到了预期的输出:
({'x': 42}, 42)
({'x': 42}, 42)

哪个是“正确”输出?(或者根据python文档,输出应该是什么?)
Here是cpython的错误报告。

最佳答案

我怀疑这与查找优化有关。从源代码:

 /* speed hack: we could use lookup_maybe, but that would resolve the
       method fully for each attribute lookup for classes with
       __getattr__, even when the attribute is present. So we use
       _PyType_Lookup and create the method only when needed, with
       call_attribute. */
    getattr = _PyType_Lookup(tp, getattr_str);
    if (getattr == NULL) {
        /* No __getattr__ hook: use a simpler dispatcher */
        tp->tp_getattro = slot_tp_getattro;
        return slot_tp_getattro(self, name);
    }

快速路径不在类字典中查找。
因此,获得所需功能的最佳方法是在类中放置一个重写方法。
class AttrDict(dict):
    """A dictionary with attribute-style access. It maps attribute access to
    the real dictionary.  """
    def __init__(self, *args, **kwargs):
        dict.__init__(self, *args, **kwargs)

    def __repr__(self):
        return "%s(%s)" % (self.__class__.__name__, dict.__repr__(self))

    def __setitem__(self, key, value):
        return super(AttrDict, self).__setitem__(key, value)

    def __getitem__(self, name):
        return super(AttrDict, self).__getitem__(name)

    def __delitem__(self, name):
        return super(AttrDict, self).__delitem__(name)

    __getattr__ = __getitem__
    __setattr__ = __setitem__

     def copy(self):
        return AttrDict(self)

我发现它能按预期工作。

关于python - Python:你定义函数__setattr__的方式不一致?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/6305267/

10-12 23:33