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

JK *_*iho 1 python recursion inheritance exception

我有一个由三个类组成的继承链: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']
Run Code Online (Sandbox Code Playgroud)

问题是,我误解了super()由子类调用的超类方法或其他一些子类化细节的使用.这个位工作正常:

>>> print foo.get_defining_fields()
['in Foo']
Run Code Online (Sandbox Code Playgroud)

bar.get_defining_fields()产生一个无限循环,运行自己直到a 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']
Run Code Online (Sandbox Code Playgroud)

所以get_defining_fields在超类中定义,并且其中的super()调用self.__class__用于尝试将正确的子类名称传递给每个子类中的调用.在Bar中调用时,它会解析为super(Bar, self).get_defining_fields()返回的列表foo.get_defining_fields()将被添加到返回的列表中far.get_defining_fields().

如果你正确理解Python的继承机制和内部工作原理可能是一个简单的错误super(),但是因为我显然没有,如果有人能指出我这样做的正确方法,我会很感激.


编辑:根据丹尼尔罗斯曼的回答,我尝试用super()这种形式替换呼叫:return super(Foo, self).get_defining_fields() + self._defining_fields()

现在无限递归不再发生,但在调用时出现了不同的错误bar.get_defining_fields():

AttributeError: 'super' object has no attribute 'get_defining_fields'
Run Code Online (Sandbox Code Playgroud)

还有别的东西还不对.


编辑:是的,终于找到了我在这里失踪的东西.将丹尼尔的最新答案标记为已接受的答案.

Dan*_*man 6

问题出在这里:

 return super(self.__class__, self)...
Run Code Online (Sandbox Code Playgroud)

self.__class__总是指当前的具体类.所以在Bar中,它指的是Bar,而在Baz中,它指的是Baz.所以,Bar调用了超类的方法,但self.__class__仍然指的是Bar,而不是Foo.所以你将获得无休止的递归.

这就是为什么你必须总是特别在super中引用这个类.像这样:

return super(Foo, self)...
Run Code Online (Sandbox Code Playgroud)

编辑权限,因为只有顶级类定义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
Run Code Online (Sandbox Code Playgroud)

¬