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 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()'
    
    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 order The actual calling order is still C, 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 class C.

    -- 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.