Search code examples
pythonpython-3.xsuperabc

Why does the super() call work in a Python class that derives from an ABC?


I have a Python ABC (let's call it A) with a concrete __init__() method, as well as a class that implements this ABC (let's call it B). As far as I understand, I should not be able to instantiate the ABC.

Question: Why and how is the call super().__init__() inside the __init__ function of class B working? I assume that super() creates an instance of the parent class - which in this case is the ABC.

Class A

from abc import ABC, abstractmethod
from util.fileManager import FileManager


class A(ABC):
    def __init__(self):
        self.file_manager = FileManager()  # Composition object

    ...

Class B, implementing class A

from interfaces.A import A


class B(A):
    def __init__(self):
        super().__init__()

    ...

Edit / Solution

What does 'super' do in Python? - difference between super().__init__() and explicit superclass __init__()

As many of you've probably seen, this question really boils down to what super() does in Python. Accepting the answer I marked, I really want to point to the link in @KarlKnechtel's comment, since it helped me further understand the situation.


Solution

  • super() does not create an instance of the parent.

    An instance of super (it's a type, not a function) provides a proxy for for some set of classes so that an attribute lookup resolves to the correct class's value. Calling super().__init__ simply starts looking in the MRO of type(self), starting at A, for an attribute named __init__.

    When arguments are explicitly provided to super, the second argument determines whose MRO you will search, and the first argument determines where in the MRO you start search (namely, the next class after the argument in the MRO).

    An example with an extremely unorthodox use of super:

    >>> class A:
    ...   def foo(self):
    ...     print("A")
    ...
    >>> class B(A):
    ...   def foo(self):
    ...     print("B")
    ...
    >>> b = B()
    >>> x = super(B, b)
    >>> type(x)
    <class 'super'>
    >>> x.__thisclass__
    <class '__main__.B'>
    >>> x.__self__ is b
    True
    >>> x.foo()
    A
    

    super itself doesn't really have optional arguments, but the context in which you almost always use it strongly imply which arguments should be passed, so Python 3 was engineered to supply them for you.