我正在使用类似于此代码的东西:
class BaseClass(object):
def __getattr__(self, attr):
return lambda:'1'
class SubClass(BaseClass):
def foo(self):
suffix = '2'
return super(SubClass, self).foo() + suffix
class SubClass2(SubClass):
def foo(self):
suffix = '3'
return super(SubClass2, self).foo() + suffix
o = SubClass2()
print o.foo()
我希望看到“123”的输出,但我却收到错误 AttributeError: 'super' object has no attribute 'foo'
。 Python 甚至没有尝试使用基类的 __getattr__
。
如果不修改基类并保持两个 super 调用相似,我无法获得我想要的输出。是否有适合我的合作 super 调用模式?
我知道 super() 以某种方式覆盖 getattr 来做它需要做的事情,但我想问的是是否有任何合理的解决方法允许子类的 __getattr__
在适当的时候被调用。
最佳答案
啊,这是个好问题!
简而言之,这里发生的是 CPython 内部偶尔需要 他们在进行属性查找时的快捷方式,这种令人惊讶的 行为是后果之一(另一个是提高绩效)。
要准确了解这种情况下发生的情况,我们需要冒险
进入 super
的定义:
http://hg.python.org/cpython/file/c24941251473/Objects/typeobject.c#l6689
特别注意它没有定义
tp_getattr
(又名 __getattr__
),但确实定义了 tp_getattro
(又名 __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
(又名 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'
关于python:__getattr__ 的协作 super 调用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18413262/