Search code examples
pythonpython-3.xoverridingmultiple-inheritancesuper

How to call "overriden" methods from both parent classes?


Consider the following classes where P1 and P2 are parent classes of C:

class P1:
    def f(self):
        print('P1')


class P2:
    def f(self):
        print('P2')


class C(P1, P2):
    def f(self):
        print('C')
        # super().f()
        # super(P1, self).f()

c = C()
c.f()

When I run this, it prints C.

If I uncomment the first line, super().f(), then it'll also print P1
because super() will call the method from the direct parent, P1

And if I uncomment the second line, super(P1, self).f(), then it'll also print P2 because super(P1, self) will call the method from P1's sibling, P2

What I want to know is that if there is any way to call f methods from both of the parent classes P1 and P2 with a single call of super() function, rather than calling it twice as I did.

Or, are there any other ways to do that without using super function?


Solution

  • Super calls the next method from the method resolution order(MRO) of the derived class.

    Every implementation of the method f should call super, the parent classes do not need to know about each other, super will automaticly call the next method in the MRO.

    Edit: I forgot that the last class in the mro is always object. object has no method called f. so you should take care that the last class in the mro that has that method, either does not call super().f or catches the AttributeError.

    As long as you follow the C3 linearization rules, The child class can change the MRO. This means the derived class determins what code gets run and what code does not get run. This is one way of dependancy injection.

    You can inspect the MRO of a class by the __mro__ atribute.

    This answer is mostly based on the talk super considered super by Raymond Hettinger

    class P1:
        def f(self):
            super().f()
            print('P1')
    
    class P2:
        def f(self):
            print('P2')
    
    class C(P1, P2):
        def f(self):
            super().f()
            print('C')
    
    class C2(P2, P1):
        def f(self):
            super().f()
            print('C2')
    
    >>> C().f()
    P2
    P1
    C
    >>> C2().f()
    P2
    C2
    >>> C.__mro__
    (<class '__main__.C'>, <class '__main__.P1'>, <class '__main__.P2'>, <type 'object'>)
    >>> C2.__mro__
    (<class '__main__.C2'>, <class '__main__.P2'>, <class '__main__.P1'>, <type 'object'>)