Python:在子类中调用超类方法时递归深度超过最大值

3
我可以帮您翻译成中文。以下是翻译的结果,保留了HTML标签。

我有一个三层继承链:Baz 继承自 Bar,Bar 继承自 Foo。我想在 Foo 类中定义一个方法,在其子类中调用时,返回其父类的输出加上自己的内容。这是期望的输出:

>>> foo = Foo()
>>> bar = Bar()
>>> baz = Baz()
>>> print foo.get_defining_fields()
['in Foo']
>>> print bar.get_defining_fields()
['in Foo', 'in Bar']
>>> print baz.get_defining_fields()
['in Foo', 'in Bar', 'in Baz']

问题在于,我对于在子类中调用超类方法时使用super()的细节或其他继承细节存在误解。以下部分是正确的:

>>> print foo.get_defining_fields()
['in Foo']

但是bar.get_defining_fields()会产生一个无限循环,一直运行自己,直到抛出RuntimeError,而不是像我想要的那样调用foo.get_defining_fields并在那里停止。

这是代码。

class Foo(object):
    def _defining_fields(self):
        return ['in Foo']

    def get_defining_fields(self):
        if self.__class__ == Foo:
            # Top of the chain, don't call super()
            return self._defining_fields()
        return super(self.__class__, self).get_defining_fields() + self._defining_fields()

class Bar(Foo):
    def _defining_fields(self):
        return ['in Bar']

class Baz(Bar):
    def _defining_fields(self):
        return ['in Baz']

所以get_defining_fields在超类中定义,它内部的super()调用使用self.__class__来尝试在每个子类中传递正确的子类名。 当在Bar中调用时,它会解析为super(Bar, self).get_defining_fields(),以便由foo.get_defining_fields()返回的列表将被预置到由far.get_defining_fields()返回的列表之前。
如果您正确理解Python的继承机制和super()的内部工作原理,那么这可能是一个简单的错误,但由于我显然不懂,所以如果有人能指出正确的方法,我将不胜感激。
编辑:根据Daniel Roseman的答案,我尝试用以下形式替换super()调用:return super(Foo, self).get_defining_fields() + self._defining_fields() 现在无限递归不再发生,但在调用bar.get_defining_fields()时出现了不同的错误。
AttributeError: 'super' object has no attribute 'get_defining_fields'

还有一些问题没有解决。


编辑: 是的,最终我弄清楚了这里缺少什么。将 Daniel 更新的答案标记为已采纳。


1
@Charles Beattie:这在Python 3中是正确的,因为所有类都是新式类,但在Python 2中并不被鼓励。 - jena
我删掉了我的评论,但之前我问他为什么要从object派生。这就是jena的回复。 - Charles Beattie
1个回答

8
问题在于这里:
 return super(self.__class__, self)...

self.__class__ 总是指向当前的具体类。因此在Bar中,它指的是Bar,在Baz中,它指的是Baz。所以,Bar调用了超类的方法,但是 self.__class__ 仍然指向Bar,而不是Foo。所以你会得到无尽的递归。

这就是为什么你必须始终在super中明确地引用类。像这样:

return super(Foo, self)...

编辑 是的,因为只有顶级类定义了get_defining_fields

你的方法是错误的。真正的解决方法并不需要使用super。我认为你可以通过迭代self.__class__.__mro__来获得更好的结果,这是访问超类方法解析顺序的方式:

class Foo(object):
    def _defining_fields(self):
        return ['in Foo']

    def get_defining_fields(self):
        fields = []
        for cls in self.__class__.__mro__:
            if hasattr(cls, '_defining_fields'):
                fields.append(cls._defining_fields(self))
        return fields

¬


那解决了递归问题,但却引发了一个 AttributeError。我相应地编辑了问题。 - JK Laiho
是的,我的想法有些混乱,我以为 Bar 会直接继承 get_defining_fields 方法,并且在 Bar 中运行它会让 super() 调用将其视为“Bar 的方法”。 - JK Laiho

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接