python multiple inheritance from different paths with same method name
Thanks for all those contributed to this thread.
To summarize:
The (currently) accepted answer is inaccurate. The correct description should be: super() is NOT ONLY good for resolving single inheritance, BUT ALSO multiple inheritance. And the reason is well explained in @blckknght 's comment:
While explicitly calling the base class methods can work for very simple scenarios like the questioner's example, it will break down if the base classes themselves inherit from a common base and you don't want the ultimate base class's method to be called twice. This is known as "diamond inheritance" and it is a big problem for many multiple inheritance systems (like in C++). Python's collaborative multiple inheritance (with super()) lets you solve easily it in many cases (though that's not to say a cooperative multiple inheritance hierarchy is easy to design or always a good idea).
The proper way, as @duncan pointed out, is to use super(), but use it consistently.
super
is indeed intended for this situation, but it only works if you use it consistently. If the base classes don't also all usesuper
it won't work, and unless the method is inobject
you have to use something like a common base class to terminate the chain ofsuper
calls.class FooBase(object): def foo(self): pass class A(FooBase): def foo(self): super(A, self).foo() print 'A.foo()' class B(FooBase): def foo(self): super(B, self).foo() print 'B.foo()' class C(A, B): def foo(self): super(C, self).foo() print 'C.foo()' C().foo() # Run this
But it is also worth to point out that, the method calling order may NOT seem intuitive at the first thought. The result is:
B.foo() A.foo() C.foo()
This seemingly strange orderThe actual calling order is stillC, A, B
, which is based on MRO. In other words,super()
will call the foo method on the"first""next" super class. This is based on the Method Resolution Order (__mro__
) for the classC
.-- Quoted and modified from @Manoj-Govindan 's answer
>>> C.__mro__ (<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <type 'object'>) >>>
As a rule of thumb, if you want to travel back to ALL parent methods but do not really care the invoke order, use
super()
consisently. Otherwise, you may choose to explicitly call parent methods in a specific order.Do not mix the usage of super() and explicit calling though. Otherwise you will end up nasty duplication like mentioned in this answer.
UPDATE: If you want to dive deeper...
In short, using super(...)
consistently in the whole class family will ensure ALL same-name methods from ancestors being called once, in the order of MRO. Such call-ALL-ancestors (rather than call-only-the-first-candidate) behavior may be conceptually easier to accept, if the method happens to be a __init__()
, see example in this blog post.
Saying "follow the MRO order" might not be very precise, though. It actually always follows the "grandchild" 's MRO, somehow. It is fascinating to see it in action. The result of following program may not be exactly what you thought it would be. Pay attention to how A.__mro__
remains same in 2 different calling stack, yet how super(A, self).name
or super(A, self).foo()
behave DIFFERENTLY when triggered by A().foo()
and by C().foo()
. See the result quoted at the end.
class FooBase(object):
name = "FooBase"
def foo(self):
print(' Base.foo() begins')
print(" My name is: %s" % self.name)
print(" My super's name is not available")
print(' Base.foo() ends')
class A(FooBase):
name = "A"
def foo(self):
print(' A.foo() begins')
print(" My name is: %s" % self.name)
print(" My super's name is: %s" % super(A, self).name)
print(" A.__mro__ is %s" % str(A.__mro__))
super(A, self).foo()
print(' A.foo() ends')
class B(FooBase):
name = "B"
def foo(self):
print(' B.foo() begins')
print(" My name is: %s" % self.name)
print(" My super's name is: %s" % super(B, self).name)
print(" B.__mro__ is %s" % str(B.__mro__))
super(B, self).foo()
print(' B.foo() ends')
class C(A, B):
name = "C"
def foo(self):
print 'C.foo() begins'
print("My name is: %s" % self.name)
print("My super's name is: %s" % super(C, self).name)
print(" C.__mro__ is %s" % str(C.__mro__))
super(C, self).foo()
print('C.foo() ends')
print("We will call A.foo()")
A().foo()
print("We will call C.foo()")
C().foo() # Run this to see how each foo() is called ONLY ONCE
And its result from Python 2.7.12 is:
We will call A.foo()
A.foo() begins
My name is: A
My super's name is: FooBase
A.__mro__ is (<class '__main__.A'>, <class '__main__.FooBase'>, <type 'object'>)
Base.foo() begins
My name is: A
My super's name is not available
Base.foo() ends
A.foo() ends
We will call C.foo()
C.foo() begins
My name is: C
My super's name is: A
C.__mro__ is (<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class '__main__.FooBase'>, <type 'object'>)
A.foo() begins
My name is: C
My super's name is: B
A.__mro__ is (<class '__main__.A'>, <class '__main__.FooBase'>, <type 'object'>)
B.foo() begins
My name is: C
My super's name is: FooBase
B.__mro__ is (<class '__main__.B'>, <class '__main__.FooBase'>, <type 'object'>)
Base.foo() begins
My name is: C
My super's name is not available
Base.foo() ends
B.foo() ends
A.foo() ends
C.foo() ends
super
is indeed intended for this situation, but it only works if you use it consistently. If the base classes don't also all use super
it won't work, and unless the method is in object
you have to use something like a common base class to terminate the chain of super
calls.
class FooBase(object):
def foo(self): pass
class A(FooBase):
def foo(self):
super(A, self).foo()
print 'A.foo()'
class B(FooBase):
def foo(self):
super(B, self).foo()
print 'B.foo()'
class C(A, B):
def foo(self):
super(C, self).foo()
print 'C.foo()'
@Marcin asks why there has to be a common base:
Without FooBase
that implements foo
but doesn't call super()
the last class that does call super()
will get an attribute error as there is no base method to call.
If there were separate base classes class A(AFooBase):
and class B(BFooBase):
the super()
call in A
would call the method in AFooBase
and the method in B
would never be called. When the base is common to all of the classes it goes to the end of the method resolution order and you can be certain that no matter how the classes are defined the base class method will be the last one called.