Python super() Function

1. Introduction to super()

The super() function in Python is used to call methods from a parent class in an inheritance hierarchy. It helps in maintaining code reusability, avoiding redundant code, and properly managing multiple inheritance scenarios.

Why do we need super()?

  • Method Overriding: When a child class overrides a method from its parent, super() allows calling the parent’s version of the method.
  • Extending Parent Methods: It allows calling the parent method without hardcoding the parent class name.
  • Constructor Chaining: Helps in calling the constructor (__init__) of the parent class when initializing a subclass.
  • Multiple Inheritance Management: Ensures proper order of method execution when dealing with multiple base classes.

2. Basic Usage of super()

Example 1: Calling Parent Class Method

class Parent:
    def greet(self):
        print("Hello from Parent")

class Child(Parent):
    def greet(self):
        super().greet()  # Calls the parent class method
        print("Hello from Child")

# Create an instance of Child
c = Child()
c.greet()

Output:

Hello from Parent
Hello from Child

What happens here?

  1. The Child class overrides the greet() method of the Parent class.
  2. Inside the Child class’s greet(), super().greet() calls the parent’s greet() method before executing the child’s version.
  3. This ensures both versions of greet() are executed.

Why use super() instead of Parent.greet(self)?

Using super() dynamically resolves the method to call based on Method Resolution Order (MRO), which is crucial in multiple inheritance.

3. Calling Parent Constructor with super()

When a subclass defines its own constructor (__init__), the parent class’s constructor is not automatically called. We must use super().__init__() to ensure proper initialization.

Example 2: Using super() to Call Parent Constructor

class Animal:
    def __init__(self, species):
        self.species = species
        print(f"Animal created: {species}")

class Dog(Animal):
    def __init__(self, name, breed):
        super().__init__("Dog")  # Calls the Parent class constructor
        self.name = name
        self.breed = breed
        print(f"Dog created: {name}, Breed: {breed}")

# Creating a Dog instance
d = Dog("Buddy", "Golden Retriever")

Output:

Animal created: Dog
Dog created: Buddy, Breed: Golden Retriever

Explanation:

  1. The Dog class has its own constructor that takes name and breed as arguments.
  2. The super().__init__("Dog") call ensures that the Animal class constructor is executed first.
  3. This allows the species attribute (from Animal) to be initialized properly before setting up name and breed in Dog.

4. Understanding Method Resolution Order (MRO)

Python follows a special order when resolving method calls in an inheritance hierarchy. This is known as the Method Resolution Order (MRO).

How to Check MRO?

You can check the MRO using:

print(ClassName.mro())

or

help(ClassName)

Example 3: Checking MRO

class A:
    def show(self):
        print("A's method")

class B(A):
    def show(self):
        print("B's method")
        super().show()

class C(B):
    def show(self):
        print("C's method")
        super().show()

c = C()
c.show()
print(C.mro())  # Prints Method Resolution Order

Output:

C's method
B's method
A's method
[<class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>]

Key Points

  1. super().show() in C calls show() from B, which then calls show() from A.
  2. MRO follows the order [C → B → A → object].
  3. object is the ultimate parent of all classes in Python.

5. super() in Multiple Inheritance

Example 4: Multiple Inheritance with super()

class A:
    def show(self):
        print("A's method")

class B(A):
    def show(self):
        print("B's method")
        super().show()

class C(A):
    def show(self):
        print("C's method")
        super().show()

class D(B, C):  # Multiple inheritance
    def show(self):
        print("D's method")
        super().show()

d = D()
d.show()

Output:

D's method
B's method
C's method
A's method

MRO for Class D

print(D.mro())  

Output:

[<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]

Key Observations

  • Order is D → B → C → A because Python uses C3 Linearization to maintain consistency.
  • super() ensures that each class is called only once, avoiding duplicate calls to A.

6. Calling super() with Arguments

Example 5: Passing Arguments

class Parent:
    def __init__(self, name):
        print(f"Parent constructor called for {name}")

class Child(Parent):
    def __init__(self, name, age):
        super().__init__(name)  # Passing 'name' to the Parent constructor
        print(f"Child constructor called for {name}, Age: {age}")

c = Child("Alice", 10)

Output:

Parent constructor called for Alice
Child constructor called for Alice, Age: 10

Key Points

  • Arguments passed to super() are forwarded to the parent class constructor.
  • This ensures that all necessary attributes are initialized properly.

7. super() in Static and Class Methods

Example 6: Using super() in Class Methods

class Parent:
    @classmethod
    def greet(cls):
        print(f"Hello from {cls.__name__}")

class Child(Parent):
    @classmethod
    def greet(cls):
        super().greet()
        print(f"Hello from {cls.__name__}")

Child.greet()

Output:

Hello from Parent
Hello from Child

Why does super() work in class methods?

  • Since class methods receive cls instead of self, super() correctly resolves methods for the class.

8. Common Mistakes with super()

Calling super() in a class without a parent

class A:
    def method(self):
        super().method()  # ERROR: No parent class

Solution: Only use super() in classes that inherit from another.

Forgetting super().__init__() in inheritance

class A:
    def __init__(self):
        print("A initialized")

class B(A):
    def __init__(self):
        print("B initialized")  # Forgot super().__init__()

b = B()

Fix: Use super().__init__() inside B.__init__().

9. Summary

  • super() calls parent class methods dynamically.
  • Works with single and multiple inheritance.
  • Resolves methods using Method Resolution Order (MRO).
  • Used in constructors, instance methods, class methods, and static methods.