How to Use `super()` in Python
super()
is a built-in function in Python that allows you to call methods from a parent or sibling class. It’s particularly useful in inheritance scenarios to extend functionality while reusing code from parent classes.
Basic Syntax
super(type, object).method(*args, **kwargs)
In Python 3, you can simplify this to just:
super().method(*args, **kwargs)
Common Use Cases
1. Extending Parent Methods
The most common use case is to extend a parent’s method while still using its functionality:
class Parent:
def greet(self):
return "Hello from Parent!"
class Child(Parent):
def greet(self):
parent_greeting = super().greet() # Call the parent's method
return f"{parent_greeting} And hello from Child too!"
2. Initializing Parent Class
Very common in __init__
methods to ensure proper initialization:
class Animal:
def __init__(self, name):
self.name = name
print(f"Animal {name} created")
class Dog(Animal):
def __init__(self, name, breed):
super().__init__(name) # Initialize the parent class
self.breed = breed
print(f"Dog {name} of breed {breed} created")
# Usage
my_dog = Dog("Buddy", "Golden Retriever")
# Output:
# Animal Buddy created
# Dog Buddy of breed Golden Retriever created
3. Multiple Inheritance
super()
becomes more complex but very powerful with multiple inheritance:
class A:
def method(self):
print("A's method")
class B(A):
def method(self):
print("B's method")
super().method()
class C(A):
def method(self):
print("C's method")
super().method()
class D(B, C):
def method(self):
print("D's method")
super().method()
# Usage
d = D()
d.method()
# Output:
# D's method
# B's method
# C's method
# A's method
Method Resolution Order (MRO)
Understanding MRO is crucial when using super()
with multiple inheritance:
class D(B, C):
pass
print(D.__mro__)
# (<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)
Best Practices
1. Always Use super() in Cooperative Inheritance
# Good
class Parent:
def __init__(self, value):
self.value = value
class Child(Parent):
def __init__(self, value, extra):
super().__init__(value) # Cooperative
self.extra = extra
2. Be Consistent with Arguments
When using super()
, ensure all methods in the inheritance chain have compatible signatures:
class A:
def method(self, *args, **kwargs):
print("A's method")
class B(A):
def method(self, *args, **kwargs):
print("B's method")
super().method(*args, **kwargs)
3. Use super() Even in Single Inheritance
Even with single inheritance, super()
is preferred over direct parent class calls:
# Good
class Child(Parent):
def method(self):
super().method()
# Avoid
class Child(Parent):
def method(self):
Parent.method(self) # Less flexible
Common Pitfalls
1. Forgetting to Call super()
class Parent:
def __init__(self, name):
self.name = name
class Child(Parent):
def __init__(self, name, age):
# Forgot super().__init__(name)
self.age = age # self.name won't be set!
2. Incorrect Argument Passing
class Parent:
def __init__(self, name):
self.name = name
class Child(Parent):
def __init__(self, name, age):
super().__init__() # Missing 'name' argument!
self.age = age
Advanced Example: Diamond Problem
class Base:
def __init__(self):
print("Base init")
class Left(Base):
def __init__(self):
print("Left init")
super().__init__()
class Right(Base):
def __init__(self):
print("Right init")
super().__init__()
class Child(Left, Right):
def __init__(self):
print("Child init")
super().__init__()
# Usage
c = Child()
# Output:
# Child init
# Left init
# Right init
# Base init
Summary
super()
provides access to methods in parent classes- It’s essential for cooperative inheritance
- Always use
super()
instead of direct parent class calls - Understand MRO when working with multiple inheritance
- Be consistent with method signatures across the inheritance chain
- Don’t forget to call
super()
when overriding methods that need parent functionality
Using super()
correctly makes your code more maintainable and flexible, especially in complex inheritance hierarchies.