Summary
- Python's super() function allows you to call superclass methods from a subclass, making it easier to implement inheritance and method overriding.
- The super() function is closely related to the Method Resolution Order (MRO) in Python, which determines the order in which ancestor classes are searched for methods or attributes.
- Using super() in class constructors is a common practice to initialize common attributes in the parent class and more specific ones in the child class. Failing to use super() can lead to unintended consequences, such as missing attribute initializations.
One of the core features of Python is its OOP paradigm, which you can use to model real-world entities and their relationships.
When working with Python classes, you’ll often use inheritance and override a superclass’s attributes or methods. Python provides a super() function that lets you call the superclass’s methods from the subclass.
What Is super() and Why Do You Need It?
Using inheritance, you can make a new Python class that inherits the features of an existing class. You can also override methods of the superclass in the subclass, providing alternative implementations. However, you might want to use the new functionality in addition to the old, rather than instead of it. In this case, super() is useful.
You can use the super() function to access superclass attributes and invoke methods of the superclass. Super is essential for object-oriented programming because it makes it easier to implement inheritance and method overriding.
How Does super() Work?
Internally, super() is closely related to the Method Resolution Order (MRO) in Python, which the C3 Linearization algorithm determines.
Here's how super() works:
- Determine the current class and instance: When you call super() inside a method of a subclass, Python automatically figures out the current class (the class containing the method that called super()) and the instance of that class (i.e., self).
- Determine the superclass: super() takes two arguments—the current class and the instance—which you don't need to pass explicitly. It uses this information to determine the superclass to delegate the method call. It does this by examining the class hierarchy and the MRO.
- Invoke the method on the Superclass: Once it’s determined the superclass, super() allows you to call its methods as if you were calling them directly from the subclass. This enables you to extend or override methods while still using the original implementation from the superclass.
Using super() in a Class Constructor
Using super() in a class constructor is common practice, since you’ll often want to initialize common attributes in the parent class and more specific ones in the child.
To demonstrate this, define a Python class, Father, which a Son class inherits from:
class Father:
def __init__(self, first_name, last_name):
self.first_name = first_name
self.last_name = last_name
class Son(Father):
def __init__(self, first_name, last_name, age, hobby):
# Call the parent class constructor (Father)
super().__init__(first_name, last_name)
self.age = age
self.hobby = hobby
def get_info(self):
return f"Son's Name: {self.first_name} {self.last_name}, \
Son's Age: {self.age}, Son's Hobby: {self.hobby}"
# Create an instance of the Son class
son = Son("Pius", "Effiong", 25, "Playing Guitar")
# Access attributes
print(son.get_info())
Inside the Son constructor, the call to super().init() invokes the Father class constructor, passing it first_name and last_name as parameters. This ensures that the Father class can still set the name attributes correctly, even on a Son object.
If you do not call super() in a class constructor, the constructor of its parent class will not run. This can lead to unintended consequences, such as missing attribute initializations or incomplete setup of the parent class's state:
...
class Son(Father):
def __init__(self, first_name, last_name, age, hobby):
self.age = age
self.hobby = hobby
...
If you now try to call the get_info method, it will raise an AttributeError because the self.first_name and self.last_name attributes have not been initialized.
Using super() in Class Methods
You can use super() in other methods, aside from constructors, in just the same way. This lets you extend or override the behavior of the superclass’s method.
class Father:
def speak(self):
return "Hello from Father"
class Son(Father):
def speak(self):
# Call the parent class's speak method using super()
parent_greeting = super().speak()
return f"Hello from Son\n{parent_greeting}"
# Create an instance of the Son class
son = Son()
# Call the speak method of the Son class
son_greeting = son.speak()
print(son_greeting)
The Son class inherits from the Father and has its speak method. The speak method of the Son class uses super().speak() to call the speak method of the Father class. This allows it to include the message from the parent class while extending it with a message specific to the child class.
Failing to use super() in a method that overrides another means the functionality present in the parent class method won’t take effect. This results in a complete replacement of the method behavior, which can lead to behavior you didn’t intend.
Understanding Method Resolution Order
Method Resolution Order (MRO) is the order in which Python searches ancestorclasses when you access a method or an attribute. MRO helps Python determine which method to call when multiple inheritance hierarchies exist.
class Nigeria():
def culture(self):
print("Nigeria's culture")
class Africa():
def culture(self):
print("Africa's culture")
class Lagos(Africa, Nigeria):
pass
city = Lagos()
city.culture()
print(Lagos.mro())
Here's what happens when you create an instance of the Lagos class and call the culture method:
- Python starts by looking for the culture method in the Lagos class itself. If it finds it, it calls the method. If not, it moves on to step two.
- If it doesn't find the culture method in the Lagos class, Python looks at the base classes in the order they appear in the class definition. In this case, Lagos inherits first from Africa and then from Nigeria. So, Python will look for the culture method in Africa first.
- If it doesn't find the culture method in the Africa class, Python will then look in the Nigeria class. This behavior continues until it reaches the end of the hierarchy and throws an error if it can't find the method in any of the superclasses.
The output shows the Method Resolution Order of Lagos, starting from left to right.
Common Pitfalls and Best Practices
When working with super(), there are some common pitfalls to avoid.
- Be mindful of the Method Resolution Order, especially in multiple inheritance scenarios. If you need to use complex multiple inheritance, you should be familiar with the C3 Linearization algorithm that Python uses to determine MRO.
- Avoid circular dependencies in your class hierarchy, which can lead to unpredictable behavior.
- Document your code clearly, especially when using super() in complex class hierarchies, to make it more understandable for other developers.
Use super() the Right Way
Python's super() function is a powerful feature when you’re working with inheritance and method overriding. Understanding how super() works and following best practices will let you create more maintainable and efficient code in your Python projects.