Python super() Function

Spread the love

The super() function in Python is a built-in function that allows you to access methods from a parent or sibling class. It is most commonly used within the context of inheritance, especially when you are overriding methods in a subclass and want to call the method from the parent class.

Understanding Inheritance and super( )

Inheritance is a fundamental concept in object-oriented programming that allows a class to inherit attributes and methods from another class. The class being inherited from is called the parent or superclass, and the class that inherits is called the child or subclass.

When overriding a method in a subclass, it is often necessary or beneficial to call the same method from the parent class. This is where super() comes in handy.

Basic Syntax of super( )

The basic syntax of super() is as follows:

super().method_name(args)

Here, method_name is the method from the parent class that you want to call, and args are the arguments that this method requires.

How super( ) Works

When you use super(), Python is actually looking for the next class in the method resolution order (MRO). The MRO is a list that defines the order in which base classes are searched when executing a method. First, it looks in the current class, and if it doesn’t find the method, it then looks in the classes that the current class inherits from, in the order they are listed in the class definition.

In a classic inheritance scenario where a subclass inherits from one superclass, super() will simply refer to the superclass.

Advantages of Using super( )

  1. Maintainability: Using super() makes your code more maintainable because you don’t have to hard-code the name of the superclass into every method call.
  2. Support for Multiple Inheritance: super() is designed to handle complex multiple inheritance scenarios seamlessly. Without super(), multiple inheritance can result in the same superclass being called more than once, leading to a variety of problems, one of which is the diamond problem.
  3. Dynamic Dispatch: super() supports dynamic dispatch (which is the selection of a method at runtime) because it doesn’t hard-code the superclass. This is essential in cases where the class hierarchy is subject to change.

Using super( ) in Single Inheritance

To understand the use of super() in single inheritance, it’s important to first grasp the basic concept of inheritance in object-oriented programming. Inheritance allows a class (known as a subclass) to inherit attributes and methods from another class (known as a superclass or parent class).

Single Inheritance

Single inheritance occurs when a subclass inherits from one superclass only. Here’s an example to illustrate single inheritance in Python:

class Parent:
    def __init__(self, value):
        self.value = value
        print("Parent class's __init__ method")

    def method_a(self):
        print("Parent class's method_a")

class Child(Parent):
    def __init__(self, value, extra):
        super().__init__(value)
        self.extra = extra
        print("Child class's __init__ method")

    def method_b(self):
        print("Child class's method_b")

In this example, Child is a subclass that inherits from Parent. The Parent class has an __init__ method that initializes the object with a value, and a method named method_a. The Child class has its own __init__ method and an additional method named method_b.

Using super( ) in __init__ Method

The Child class uses super() to call the __init__ method of the Parent class. This is crucial because it allows the Child class to initialize part of its state in terms of the Parent class’s initialization. Here is what happens when you create an instance of Child:

child_instance = Child(10, 'extra data')
  1. The __init__ method of Child is called with 10 and 'extra data' as arguments.
  2. Inside Child‘s __init__ method, super().__init__(value) is called. The super() function returns a temporary object of the superclass (Parent) which then allows you to call its methods.
  3. The Parent class’s __init__ method is called with 10 as the argument, setting self.value to 10.
  4. Control returns to the Child class’s __init__ method, and the self.extra attribute is set to 'extra data'.
  5. Finally, the message “Child class’s init method” is printed.

This sequence of operations ensures that the Child object is properly initialized with the state defined in both the Parent and Child classes.

Using super( ) with Other Methods

You can also use super() to call other methods of the superclass, not just __init__. This is often used when you want to extend or modify the behavior of a method from the superclass. Here’s how it’s done:

class Parent:
    def method_a(self):
        print("Parent class's method_a")
class Child(Parent):
    def method_a(self):
        super().method_a()  # Calls the same method from the Parent class
        print("Extension by Child class's method_a")

child_instance = Child()
child_instance.method_a()

When you call method_a on an instance of Child, here is what will happen:

  1. The overridden method_a of the Child class is executed.
  2. Inside this method, super().method_a() calls the method_a method from the Parent class, which will print “Parent class’s method_a”.
  3. After the super() call, the next line print("Extension by Child class's method_a") will execute, printing “Extension by Child class’s method_a”.

So, the output will be:

Parent class's method_a
Extension by Child class's method_a

Using super( ) in Multiple Inheritance

Multiple inheritance is a feature in object-oriented programming where a class can inherit attributes and methods from more than one parent class. Python supports multiple inheritance and provides the super() function to allow you to call methods in a parent class in the context of multiple inheritance.

How Multiple Inheritance Works in Python

In Python, when a class inherits from multiple parent classes, it creates a complex hierarchy where the interpreter needs a way to determine which parent class to refer to when a method is called. This is resolved using the Method Resolution Order (MRO).

The MRO is a rule-based system that Python uses to determine the order in which to look up a method. It’s a linearization of the hierarchy tree. Python uses the C3 linearization algorithm to determine the MRO in multiple inheritance scenarios.

Using super( ) in Multiple Inheritance

The super() function is used to call the next method in the MRO. Here’s an example that illustrates the use of super() in multiple inheritance:

class Base:
    def __init__(self):
        print("Base init")

class A(Base):
    def __init__(self):
        super().__init__()
        print("A init")

class B(Base):
    def __init__(self):
        super().__init__()
        print("B init")

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

In the above classes:

  • Base is the base class.
  • A and B are subclasses that inherit from Base.
  • C inherits from both A and B.

When you create an instance of C, the __init__ method of C is called, which in turn uses super() to call the __init__ method of the next class in the MRO, which is A. A‘s __init__ method is then called, which also calls super(), leading to B‘s __init__ method, and so on until the base class’s __init__ method is reached.

MRO and super( )

The MRO for class C will look like this: [C, A, B, Base, object]. Here’s how the MRO affects the calls:

  1. C.__init__() calls A.__init__(), because A is the first class after C in the MRO.
  2. A.__init__() calls B.__init__(), not Base.__init__(), because of how the MRO is computed.
  3. B.__init__() then calls Base.__init__(), which is the next in line in the MRO.
  4. Finally, Base.__init__() executes, and the initialization chain is complete.

Benefits of Using super( ) in Multiple Inheritance

  1. No Hardcoding Required: super() eliminates the need to hardcode the superclass’s name, which can make changes to the inheritance hierarchy easier.
  2. Consistent Method Resolution: super() follows the MRO, ensuring that the methods are called in the order Python intends based on the class hierarchy.
  3. Avoids Duplicating Calls: In complex inheritance hierarchies, super() prevents the same superclass method from being called multiple times unintentionally.

Practical Example

Imagine you have a system where a C class represents a widget that inherits from two classes: A for touch functionality and B for drawable functionality:

class Touch:
    def __init__(self):
        print("Touch capability added")

class Drawable:
    def __init__(self):
        print("Drawable capability added")

class Widget(Touch, Drawable):
    def __init__(self):
        super().__init__()
        print("Widget created with both capabilities")

When an instance of Widget is created, it will print out the capabilities as they are initialized:

widget = Widget()

Output:

Touch capability added
Widget created with both capabilities

The output will reflect the order defined by the MRO of Widget.

In multiple inheritance, super() is an essential tool for calling superclass methods in a way that respects the MRO and avoids common pitfalls associated with multiple inheritance, such as duplicated method calls or the diamond problem. It ensures that each method in the inheritance hierarchy is called in a predictable and controlled manner, which is crucial for maintaining clean and functional object-oriented code in Python.

Conclusion

The super() function is a powerful feature of Python that facilitates a cooperative method call dispatching mechanism. It is a critical part of the language’s support for inheritance, allowing for a clean and maintainable way to invoke superclass methods. The super() function not only handles simple single inheritance but is also designed to operate correctly in complex scenarios with multiple inheritance, ensuring that each superclass’s methods are called in the order defined by the MRO. This makes super() an essential tool for any Python programmer working with object-oriented code.

Leave a Reply