啊,这是一个很好的问题!
简而言之,这里发生的情况是 CPython 内部在进行属性查找时偶尔会走捷径,这种令人惊讶的行为是后果之一(另一个是提高性能)。
要准确了解在这种情况下发生了什么,我们需要冒险进入以下定义super
:
http ://hg.python.org/cpython/file/c24941251473/Objects/typeobject.c#l6689
请特别注意,它没有定义
tp_getattr
(aka __getattr__
),但确实定义了tp_getattro
(aka __getattribute__
):
PyTypeObject PySuper_Type = {
PyVarObject_HEAD_INIT(&PyType_Type, 0)
"super", /* tp_name */
...
0, /* tp_getattr */
...
super_getattro, /* tp_getattro */
...
};
(回想一下,每次请求属性时都会调用它,而不是__getattribute__
,它仅在对象上不存在该属性时才调用(粗略地说:如果该属性不在对象的 中
))。__getattr__
__dict__
接下来,查看
super_getattro
(aka super.__getattribute__
) 的定义,我们可以看到其实现
大致为:
class super(object):
def __init__(self, obj_type, obj):
self.obj_type = obj_type
self.obj = obj
def __getattribute__(self, attr):
i = self.obj_type.__mro__.find(self.obj_type)
i += 1
while i < len(obj_type.__mro__):
cur_type = self.obj_type.__mro__[i]
cur_dict = cur_type.__dict___
res = cur_dict.get(attr)
if res is not None:
return res
i += 1
return object.__getattribute__(self, attr)
这很明显为什么super
不能很好地使用__getattr__
-
super
只检查父类中的属性' __dict__
!
有趣的是:看起来pypy
(从 2.1.0 开始)的行为方式相同:
$ pypy super.py
Traceback (most recent call last):
File "app_main.py", line 72, in run_toplevel
File "super.py", line 16, in <module>
print o.foo()
File "super.py", line 13, in foo
return super(SubClass2, self).foo() + suffix
File "super.py", line 8, in foo
return super(SubClass, self).foo() + suffix
AttributeError: 'super' object has no attribute 'foo'