Search code examples
pythonoopsuper

What does it mean when a parent class has a super()?


As far as i know, super() is used to call the methods overwritten in the subclass (like an overwritten __init__ in the subclass).

Also, i know that all python classes inherit from a special class called object.

While studying OOP i ran into a weird example:

class A(object):
    def __init__(self):
        print('A.__init__')
        super().__init__()

class B(object):
    def __init__(self):
        print('B.__init__')
        super().__init__()

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

I understand most of this except...why does class A has a super().__init__()? It's the parent class, it shouldn't need to inherit from anyone. And the special object class doesn't do anything so i don't know why it would need it.

This is not the only case i have seen a super in the parent class, in a previous question i made, a solution to aproblem involved it but i didn't understood it. I also can't find an easy explanation of this usage of super() inheritance.

In simple terms...why would i need to use super() in a parent class?


Solution

  • Consider the following example:

    class A:
        def __init__(self, a, **kwargs):
            print('A.__init__ called')
            self.a = a
            super().__init__(**kwargs)
            print('A.__init__ finished')
    
    class B:
        def __init__(self, b, **kwargs):
            print('B.__init__ called')
            self.b = b
            super().__init__(**kwargs)
            print('B.__init__ finished')
    
    class C(A, B):
        def __init__(self, c, **kwargs):
            print('C.__init__ called')
            self.c = c
            super().__init__(**kwargs)
            print('C.__init__ finished')
    

    When you create a C object, the output is as follows:

    >>> C(a=1, b=2, c=3)
    C.__init__ called
    A.__init__ called
    B.__init__ called
    B.__init__ finished
    A.__init__ finished
    C.__init__ finished
    <__main__.C object at 0x7fd15abcab70>
    

    From the order that the __init__ methods are called, and the fact that they finish in reverse order, we can see that C.__init__ calls A.__init__, which calls B.__init__. That is, although A has no explicit parent class (so its direct parent is object), the super().__init__ call in A actually calls B.__init__.

    This is because B is the next class after A in C's method resolution order (MRO). The A class needs to call super() because although its superclass is object, Python allows multiple inheritance, so A cannot guarantee that it is the last class before object in the MRO.

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

    See Raymond Hettinger's article Python's super() considered super! for a more detailed explanation about the MRO and "cooperative multiple inheritance techniques".