Python: Exceeding max recursion depth while calling superclass method from subclass
I've got an inheritance chain of three classes: Baz inherits from Bar inherits from Foo. I'd like to define a method in the Foo class that, when called in a subclass, returns the output of its parent class appended with stuff of its own. This is the desired output:
>>> 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']
The problem is, I'm misunderstanding something about the use of super()
in superclass methods that are called by subclasses, or some other subclassing detail. This bit works fine:
>>> print foo.get_defining_fields()
['in Foo']
But bar.get_defining_fields()
produces an infinite loop, running itself until a RuntimeError
is raised instead of calling out to foo.get_defining_fields
and stopping there like I want it to.
Here's the code.
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']
So get_defining_fields
is defined in the superclass, and the super()
call within it uses self.__class__
in an attempt to pass the correct subclass name to the call in each subclass. When called in Bar, it resolves to super(Bar, self).get_defining_fields()
so that the list returned by foo.get_defining_fields()
would be pr开发者_如何学Pythonepended to the one returned by far.get_defining_fields()
.
Probably a simple mistake if you properly understand Python's inheritance mechanics and the inner workings of super()
, but since I obviously don't, I'd appreciate it if someone could point me to the proper way of doing this.
EDIT: as per Daniel Roseman's answer, I tried replacing the super()
call with this form: return super(Foo, self).get_defining_fields() + self._defining_fields()
Now the infinite recursion no longer occurs, but I get a different error when calling bar.get_defining_fields()
:
AttributeError: 'super' object has no attribute 'get_defining_fields'
Something else is still amiss.
EDIT: yep, finally figured out what I was missing here. Marked Daniel's updated answer as the accepted one.
The problem is here:
return super(self.__class__, self)...
self.__class__
always refers to the current concrete class. So in Bar, it refers to Bar, and in Baz, it refers to Baz. So, Bar calls the superclass's method, but the self.__class__
still refers to Bar, not Foo. So you will get an endless recursion.
That's why you must always refer to the class specifically in super. Like this:
return super(Foo, self)...
Edit Right, because only the top class defines get_defining_fields
.
You're going about this the wrong way, really. This isn't a job for super
at all. I think you might get better results iterating through self.__class__.__mro__
, which is the way to access the method resolution order of the superclasses:
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
¬
精彩评论