Alb*_*ert 15 python setattr getattr
考虑以下代码:
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)
Run Code Online (Sandbox Code Playgroud)
我期望相同的输出.但是,使用CPython 2.5,2.6(类似于3.2),我得到:
({'x': 42}, 42)
({}, 42)
Run Code Online (Sandbox Code Playgroud)
使用PyPy 1.5.0,我得到了预期的输出:
({'x': 42}, 42)
({'x': 42}, 42)
Run Code Online (Sandbox Code Playgroud)
哪个是"正确的"输出?(或者根据Python文档应该输出什么?)
这是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);
}
Run Code Online (Sandbox Code Playgroud)
快速路径不会在类字典中查找它.
因此,获得所需功能的最佳方法是在类中放置重写方法.
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)
Run Code Online (Sandbox Code Playgroud)
我发现它按预期工作.