python multiple inheritance from different paths with same method name

Question:

With the following code sample, can super be used, or C has to call A.foo and B.foo explicitly?

class A(object):
    def foo(self):
        print 'A.foo()'

class B(object):
    def foo(self):
        print 'B.foo()'

class C(A, B):
    def foo(self):
        print 'C.foo()'
        A.foo(self)
        B.foo(self)
Asked By: sayap

||

Answers:

If you added the following:

class A(object):
    def foo(self):
        print 'A.foo()'

class B(object):
    def foo(self):
        print 'B.foo()'

class C(A, B):
    def foo(self):
        super(C, self).foo()
        print 'C.foo()'
        A.foo(self)
        B.foo(self)


c = C()
c.foo()

Then super(C, self).foo() refers to A.foo

The output is

A.foo()
C.foo()
A.foo()
B.foo()

[Edit: Include additional information and links]

Answered By: pyfunc

super() will only ever resolve a single class type for a given method, so if you’re inheriting from multiple classes and want to call the method in both of them, you’ll need to do it explicitly. i.e.A.foo(self)

Answered By: Amber

Super will call the foo method on the “first” super class. This is based on the Method Resolution Order (__mro__) for the class C.

>>> C.__mro__
(<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <type 'object'>)
>>> 

Therefore if you call super(C, self).foo(), A.foo is called. If you change the inheritance order to class C(B, A): then this is reversed. The __mro__ now looks like:

>>> C.__mro__
(<class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <type 'object'>)
>>> 

If you call super(C, self).foo() after making this change, B.foo() will get called.

Answered By: Manoj Govindan

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.

Answered By: Duncan

This is possible with super

class A(object):
    def foo(self):
        print 'A.foo()'

class B(object):
    def foo(self):
        print 'B.foo()'

class C(A, B):
    def foo(self):
        print 'C.foo()'
        super(C, self).foo() # calls A.foo()
        super(A, self).foo() # calls B.foo()
Answered By: acktus

As Duncan said above, you can get predictable results especially if you use super() consistently.

The results from the following test were helpful to me:

class FooBase(object):
    def foo(self):
        print 'FooBase.foo()'

class A(FooBase):
    def foo(self):
        print 'A.foo() before super()'
        super(A, self).foo()
        print 'A.foo() after super()'

class B(FooBase):
    def foo(self):
        print 'B.foo() before super()'
        super(B, self).foo()
        print 'B.foo() after super()'

class C(A, B):
    def foo(self):
        print 'C.foo() before super()'
        super(C, self).foo()
        print 'C.foo() after super()'

This will print out:

>>> c = C()
>>> c.foo()
C.foo() before super()
A.foo() before super()
B.foo() before super()
FooBase.foo()
B.foo() after super()
A.foo() after super()
C.foo() after super()
Answered By: bmzf

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
Answered By: RayLuo
class A:
    def article(self):
        print("Class A")

class B:
    def article(self):
        print("Class B")

class C(A,B):
    def article(self):
        print("Class C ::n")
        super().article()

x = C()
x.article()

output:

Class C ::

Class A

Answered By: omar ahmed

Here you can see how the classes with same methods are called:-

Here, in method ‘feat()’ of class D(A,B,C)

super(D, self).feature2() —-> will call the method (feature2) of class A.

super(A, self).feature2() —-> will call the method (feature2) of class B.

super(B, self).feature2() —-> will call the method (feature2) of class C.

class A:

    def __init__(self):
        print("in A Init")

    def feature1(self):
        print("Feature 1-A working")

    def feature2(self):
        print("Feature 2-A working")

class B:

    def __init__(self):
        print("in B Init")

    def feature1(self):
        print("Feature 1-B working")

    def feature2(self):
        print("Feature 2-B working")

class C:

    def __init__(self):
        print("in C Init")

    def feature1(self):
        print("Feature 1-C working")

    def feature2(self):
        print("Feature 2-C working")

    

class D(A,B,C):

    def __init__(self):
        super().__init__()
        print("in D init")


    def feat(self):
        super(D, self).feature2()
        super(A, self).feature2()
    
        print('nn')

        ### B().feature2() is calling explicitly, but 
        ### super(A, self).feature2() is not.
        
        ### Both will give same result in below,
        ### It will print True below

        print(B().feature2() == super(A, self).feature2())

        print('nn')
    
        super(B, self).feature2()
        print(D.__mro__)


a1 = D()
a1.feat()

The result is:-

in A Init
in D init
Feature 2-A working
Feature 2-B working



in B Init
Feature 2-B working
Feature 2-B working
True



Feature 2-C working
(<class '__main__.D'>, <class '__main__.A'>, <class '__main__.B'>, <class 
'__main__.C'>, <class 'object'>)
Answered By: space_boy
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.