13

我正在使用类似于此代码的东西:

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() 以某种方式覆盖getattr来做它需要做的事情,但我问是否有任何合理的解决方法允许__getattr__在适当的时候调用子类。

4

2 回答 2

5

啊,这是一个很好的问题!

简而言之,这里发生的情况是 CPython 内部在进行属性查找时偶尔会走捷径,这种令人惊讶的行为是后果之一(另一个是提高性能)。

要准确了解在这种情况下发生了什么,我们需要冒险进入以下定义superhttp ://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'
于 2013-08-24T01:24:36.137 回答
3

这似乎工作正常。我目前不明白为什么标准的超类不这样做。

class super2(super):
    def __getattr__(self, attr):
        return self.__self__.__getattr__(attr)

class BaseClass(object):
    def __getattr__(self, attr):
        return lambda:'1'

class SubClass(BaseClass):
    def foo(self):
        suffix = '2'
        return super2(SubClass, self).foo() + suffix

class SubClass2(SubClass):
    def foo(self):
        suffix = '3'
        return super2(SubClass2, self).foo() + suffix

o = SubClass2()
print o.foo()
于 2013-08-23T23:46:12.930 回答