Skip to main content
 首页 » 编程设计

Python:从子类调用父类(super class)方法时超出最大递归深度

2025年05月04日19cmt

我有一个包含三个类的继承链:Baz 继承自 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 class)方法中使用 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 class)中定义,其中的 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 的更新答案标记为已接受。

请您参考如下方法:

问题出在这里:

 return super(self.__class__, self)... 

self.__class__ 始终引用当前 具体类。所以在Bar中指的是Bar,在Baz中指的是Baz。因此,Bar 调用父类(super class)的方法,但 self.__class__ 仍然引用 Bar,而不是 Foo。所以你会得到一个无穷无尽的递归。

这就是为什么您必须始终在 super 中专门引用类。像这样:

return super(Foo, self)... 

编辑 正确,因为只有顶级类定义了 get_defining_fields。

你的做法是错误的,真的。这根本不是 super 的工作。我认为您可能会通过 self.__class__.__mro__ 迭代获得更好的结果,这是访问父类(super class)的方法解析顺序的方式:

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 

¬