The `super()` function in Python is a powerful tool that allows you to call methods from a parent class within a child class. It is particularly useful in the context of inheritance, where a child class extends the functionality of a parent class. Understanding how `super()` operates is crucial for effective class design and can help avoid common pitfalls associated with method resolution order (MRO) and multiple inheritance.
When you use `super()`, it returns a temporary object of the superclass that allows you to call its methods. This is especially important in complex class hierarchies where you want to ensure that the correct method is invoked, adhering to the MRO. Let’s explore how `super()` works in detail, along with practical examples and best practices.
The simplest use of `super()` is within a method of a child class. Here’s a basic example:
class Parent:
def __init__(self):
print("Parent's __init__")
def show(self):
print("Parent's show method")
class Child(Parent):
def __init__(self):
super().__init__() # Calls Parent's __init__
print("Child's __init__")
def show(self):
super().show() # Calls Parent's show method
print("Child's show method")
child_instance = Child()
child_instance.show()
In this example, when we create an instance of `Child`, it first calls the `__init__` method of `Parent` due to the `super().__init__()` call. The output will be:
Parent's __init__
Child's __init__
When we call `child_instance.show()`, it first executes `Parent`'s `show` method, followed by `Child`'s `show` method:
Parent's show method
Child's show method
One of the key benefits of using `super()` is that it respects the method resolution order (MRO). The MRO determines the order in which base classes are looked up when searching for a method. You can view the MRO of a class using the `__mro__` attribute:
print(Child.__mro__)
This will output a tuple showing the order in which classes are searched for methods:
(<class '__main__.Child'>, <class '__main__.Parent'>, <class 'object'>)
In cases of multiple inheritance, `super()` can help avoid the diamond problem, where a method could be called from multiple parent classes. Consider the following example:
class A:
def __init__(self):
print("A's __init__")
class B(A):
def __init__(self):
super().__init__()
print("B's __init__")
class C(A):
def __init__(self):
super().__init__()
print("C's __init__")
class D(B, C):
def __init__(self):
super().__init__()
print("D's __init__")
d_instance = D()
The output will be:
A's __init__
C's __init__
B's __init__
D's __init__
This demonstrates how `super()` follows the MRO to ensure that each parent class's `__init__` method is called exactly once.
In conclusion, `super()` is a fundamental aspect of class inheritance in Python that allows for cleaner and more maintainable code. By understanding its usage, MRO, and best practices, developers can leverage its capabilities effectively in their applications.