Reputation: 5039
I was just trying something with multiple inheritance in python. I come up with this
class ParentOne:
def foo(self):
print("ParentOne foo is called")
class ParentTwo:
def foo(self):
print("ParentTwo foo is called")
class Child(ParentOne, ParentTwo):
# how is this working
def call_parent_two_foo(self):
super(ParentOne, self).foo()
# This does not work
def call_parent_foo(self):
super(ParentTwo, self).foo()
def call_super_foo(self):
super(Child, self).foo()
def foo(self):
print("Child foo is called")
if __name__ == "__main__":
child = Child()
child.foo()
child.call_super_foo()
child.call_parent_two_foo()
# child.call_parent_foo() #This gives the below error
# super(ParentTwo, self).foo()
# AttributeError: 'super' object has no attribute 'foo'
and it gives the following output
Child foo is called
ParentOne foo is called
ParentTwo foo is called
I am getting confused as to how calling of super(ParentOne, self).foo()
is evaluated in this case. As per my understanding ParentOne
class does not have any idea of the methods and attributes of ParentTwo
class. How does super works in case of multiple inheritance
Upvotes: 4
Views: 4860
Reputation: 11
class X1:
def run(self):
print("x1")
class X2:
def run(self):
print("x2")
class X3:
def run(self):
print("x3")
class X2:
def run(self):
print("x2")
class Y(X1, X2, X3):
def run(self):
print("y")
Given an instance:
y = Y()
To call base class function:
super(Y,y).run()
super(X1,y).run()
super(X2,y).run()
y.run()
Output
x1
x2
x3
y
Similarity,
super(Y, y).run()
for cls in y.__class__.__bases__:
if(cls != X3):
super(cls,y).run()
y.run()
Output
x1
x2
x3
y
Upvotes: 1
Reputation: 309929
Python constructs a method resolution order (MRO) when it builds a class. The MRO is always linear. If python cannot create a linear MRO, then a ValueError
will be raised. In this case, your MRO probably looks like:
Child -> ParentOne -> ParentTwo -> object
Now when python see's a super(cls, self)
, it basically looks at self
and figures out the MRO. It then uses cls
to determine where we are currently at in the MRO and finally it returns an object which delegates to the next class in the MRO. So, in this case, a super(Child, self)
call would return an object that delegates to ParentOne
. A super(ParentOne, self)
class would return an object that delegates to ParentTwo
. Finally a super(ParentTwo, self)
call would delegate to object
. In other words, you can think of super
as a fancier version of the following code:
def kinda_super(cls, self):
mro = inspect.getmro(type(self))
idx = mro.index(cls)
return Delegate(mro[idx + 1]) # for a suitably defined `Delegate`
Note that since super(ParentTwo, self)
returns a "Delegate" to object
, we can see why you're getting an AttributeError
when you try super(ParentTwo, self).foo()
-- Specifically the reason is because object
has no foo
method.
Upvotes: 11
Reputation: 9765
You may understand Child(ParentOne, ParentTwo)
as two separate inheritances within a chain: Child(ParentOne(ParentTwo))
. Actually, ParentOne
doesn't inherit ParentTwo
, they are two separate classes, but the method super
works like there's a chain of inheritances (in case of multiple inheritance only). I like this example to understand better what's going on (for Python 3.x):
class P:
def m(self):
print("P")
class A(P):
def m(self):
super().m() # -> B, if we inherit like C(A, B)
print("A")
class B(P):
def m(self):
super().m() # -> P, if we inherit like C(A, B)
print("B")
class C(A, B):
def m(self):
super().m() # -> A
print("C")
A.m(self)
B.m(self)
c = C()
c.m()
It also considers a case if two parents inherit one base class. The script above prints:
P
B
A
C
P
B
A
P
B
Upvotes: 0