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)
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]
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)
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.
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.
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()
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()
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
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
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'>)
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)
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]
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)
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.
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.
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()
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()
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
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
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'>)