Anurag Sharma
Anurag Sharma

Reputation: 5039

python multiple inheritance, calling base class function

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

Answers (3)

Anh-Dung Vo
Anh-Dung Vo

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

mgilson
mgilson

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

Fomalhaut
Fomalhaut

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

Related Questions