DEV Community

Bhavani Ravi
Bhavani Ravi

Posted on • Originally published at bhavaniravi.Medium on

Python super() vs Base.__init__ Method

When defining a subclass, there are different ways to call the __init__ method of a parent class. Let’s start with a base class and go through each of these methods.

For this blog, it’s better that you open a sample.py python file and follow along.

class Base(object):
    def __init__ (self):
        print "Base created"
Enter fullscreen mode Exit fullscreen mode

Method 1 :: Using Parent Reference Directly

class ChildA(Base):
    def __init__ (self):
        Base. __init__ (self)
        print ("Child A initlaized")
Enter fullscreen mode Exit fullscreen mode

Method 2:: Using Super with child class

class ChildB(Base):
    def __init__ (self):
        print ("Child B initlaized")
        super(ChildB, self). __init__ ()
Enter fullscreen mode Exit fullscreen mode

Method 3:: Using the super method

class ChildC(Base):
    def __init__ (self):
        super(). __init__ ()
        print ("Child C initlaized")
Enter fullscreen mode Exit fullscreen mode

Questions

  1. What are the pros and cons of each method?
  2. Is there one single right way to do this?

When you run this code as a single Python script, initializing child classes A, B, and C., You will notice absolutely no difference.

cA = ChildA()
cB = ChildB()
cC = ChildC()
Enter fullscreen mode Exit fullscreen mode

How can we demystify this? Let’s start with the documentation.

  1. As of Python3 super() is same as super(ChildB, self).__init__(). That rules out one of the three methods.
  2. To compare Base.__init__(self) and super().__init__() we need multiple Inheritance. Consider the following snippet
class Base1:
    def __init__ (self):     
        print ("Base 1 created")     
        super(). __init__ ()

class Base2:
    def __init__ (self):     
        print ("Base 2created")     
        super(). __init__ ()
Enter fullscreen mode Exit fullscreen mode

Let’s write the subclasses

class A1(Base1, Base2):
    def __init__ (self):     
        print ("Child A1 Initialized")     
        super(). __init__ ()

class A2(Base2, Base1):
    def __init__ (self):     
        print ("Child A Initialized")     
        super(). __init__ ()
Enter fullscreen mode Exit fullscreen mode

Let’s initialize the objects

a1 = A1()
print ("\n\n")
a2 = A2()
Enter fullscreen mode Exit fullscreen mode

On running the above snippet, we get the following Output.

Base 1 created
Base 2 created
Child A1 initialized

Base 2 created
Base 1 created
Child A2 initialized
Enter fullscreen mode Exit fullscreen mode

In the case of class A1(Base1, Base2) Base1 is initialized first, followed by Base2. It’s the inverse for class A2. We can conclude that the methods are called based on the order of specification.

  1. When you use the Base1.__init__() method, you lose out on this feature of Python
  2. When you introduce new hierarchies, renaming the classes will become a nightmare

So how does Python know which function to call first, introducing MRO(Method Resolution Order)

Method Resolution Order

Method Resolution Order(MRO) denotes the way a programming language resolves a method or attribute.

In the case of single inheritance, the attribute is searched only at a single level, with multiple inheritance Python interpreter looks for the attribute in itself then its parents in the order of inheritance. In case of A1 -> Base 1 -> Base 2

One can use the mro function to find the method resolution order of any particular class.

print (A2.mro())
Enter fullscreen mode Exit fullscreen mode

** Output**

[<class ' __main__.A2'>, <class ' __main__.Base2'>, <class ' __main__.Base1'>, <class 'object'>]
Enter fullscreen mode Exit fullscreen mode

The Last Punch

Comment out the super calls in base class and check the ouput of your script.

class Base1:
    def __init__ (self):
        print ("Base 1 created")
        # super(). __init__ () 

class Base2:
    def __init__ (self):
        print ("Base 2 created")
        # super(). __init__ ()
Enter fullscreen mode Exit fullscreen mode

What do you see?

Base 1 created
Child A1 initlaized
Base 2 created
Child A2 initlaized

[<class ' __main__.A2'>, <class ' __main__.Base2'>, <class ' __main__.Base1'>, <class 'object'>]
Enter fullscreen mode Exit fullscreen mode

In spite of having Base1 and Base2 in the MRO list, mro won’t resolve the order unless the super() function is propagated all the way up to the base class, i.e., Python propagates the search for the attribute only until it finds one. Comment on the init method in Base1 and see for yourself.

class Base1:
    pass

    # def __init__ (self):
    # self.prop1 = "Base 1"
    # self.prop11 = "Base 11"
    # print ("Base 1 created")
    # # super(). __init__ ()
Enter fullscreen mode Exit fullscreen mode

Output

Since Python can’t find the __init__ method in Base1 it checks Base2 before sending it all the way to object class

Base 2 created
Child A1 initlaized

Base 2 created
Child A2 initlaized
[<class ' __main__.A2'>, <class ' __main__.Base2'>, <class ' __main__.Base1'>, <class 'object'>]
Enter fullscreen mode Exit fullscreen mode

People ask me why I love Python so much. It’s not because Python is simple and easy. It is all these things Python does to make things easy for us, sometimes a little hard, too :)

_Want to build a project with Python, Join the Python to Project Bootcamp

Top comments (0)