How to Use Method Overriding in Python
Method overriding is a core feature of object-oriented programming that allows a child class (subclass) to provide its own implementation of a method that is already defined in its parent class (superclass). When the child class defines a method with the same name and parameters as one in the parent class, the child's version overrides the parent's version for instances of the child class.
This mechanism is essential for achieving polymorphism, which is the ability for different classes to be used interchangeably through a common interface while each providing its own specific behavior.
In this guide, you will learn how method overriding works in Python, see examples with single, multiple, and multilevel inheritance, and understand how to call the parent class's method from within the overridden method using super().
How Method Overriding Works
When you call a method on an object, Python uses the type of the object (not the variable reference) to determine which version of the method to execute:
- If the object is an instance of the parent class, the parent's method runs.
- If the object is an instance of the child class and the child has overridden the method, the child's method runs.
class Parent:
def __init__(self):
self.value = "Inside Parent"
def show(self):
print(self.value)
class Child(Parent):
def __init__(self):
super().__init__() # Call parent constructor
self.value = "Inside Child"
def show(self):
print(self.value)
# Parent object calls Parent's show()
obj1 = Parent()
obj1.show()
# Child object calls Child's show()
obj2 = Child()
obj2.show()
Output:
Inside Parent
Inside Child
The Child class defines its own show() method, which overrides the parent's show(). When obj2.show() is called, Python finds show() in the Child class first and executes that version.
super().__init__()?Calling super().__init__() in the child's constructor ensures that any initialization logic in the parent class runs before the child adds or modifies attributes. Without it, parent-defined attributes may not exist, leading to AttributeError at runtime.
Basic Method Override Example
Here is a clear example showing the override in isolation:
class Animal:
def speak(self):
return "Some generic sound"
class Dog(Animal):
def speak(self):
return "Woof!"
class Cat(Animal):
def speak(self):
return "Meow!"
# Each subclass provides its own implementation
animals = [Animal(), Dog(), Cat()]
for animal in animals:
print(f"{animal.__class__.__name__}: {animal.speak()}")
Output:
Animal: Some generic sound
Dog: Woof!
Cat: Meow!
All three classes share the same method name speak(), but each provides a different implementation. This is polymorphism enabled by method overriding.
Method Overriding With Multiple Inheritance
In multiple inheritance, a child class inherits from more than one parent. The child can override methods from any of its parents:
class Parent1:
def show(self):
print("Inside Parent1")
class Parent2:
def display(self):
print("Inside Parent2")
class Child(Parent1, Parent2):
# Override only Parent1's show() method
def show(self):
print("Inside Child")
obj = Child()
obj.show() # Calls Child's overridden version
obj.display() # Calls Parent2's version (not overridden)
Output:
Inside Child
Inside Parent2
The Child class overrides show() from Parent1 but inherits display() from Parent2 without modification.
When multiple parent classes define the same method, Python uses the MRO (Method Resolution Order) to determine which version to call. You can inspect the MRO with:
print(Child.__mro__)
# (<class 'Child'>, <class 'Parent1'>, <class 'Parent2'>, <class 'object'>)
Python searches for the method in this order: Child -> Parent1 -> Parent2 -> object. The first match wins.
Method Overriding With Multilevel Inheritance
In multilevel inheritance, classes form a chain: grandchild -> child -> parent. Each level can override methods from any ancestor:
class Parent:
def display(self):
print("Inside Parent")
class Child(Parent):
def show(self):
print("Inside Child")
class GrandChild(Child):
# Overrides Child's show() method
def show(self):
print("Inside GrandChild")
g = GrandChild()
g.show() # GrandChild's version (overrides Child)
g.display() # Parent's version (inherited through the chain)
Output:
Inside GrandChild
Inside Parent
GrandChild overrides show() from Child, but display() is inherited from Parent through the chain.
Calling the Parent's Method From the Overridden Method
Often, you want to extend the parent's behavior rather than completely replace it. Python provides two ways to call the parent's method from within the child's overridden method.
Using super() (The Recommended Approach)
The super() function returns a proxy object that delegates method calls to the parent class. This is the preferred approach because it works correctly with multiple inheritance and follows the MRO:
class Parent:
def show(self):
print("Inside Parent")
class Child(Parent):
def show(self):
super().show() # Call parent's show() first
print("Inside Child") # Then add child-specific behavior
obj = Child()
obj.show()
Output:
Inside Parent
Inside Child
Using the Parent Class Name Directly
You can also call the parent's method by referencing the class name explicitly. This works but is less flexible than super() because it hardcodes the parent class name:
class Parent:
def show(self):
print("Inside Parent")
class Child(Parent):
def show(self):
Parent.show(self) # Explicit parent class reference
print("Inside Child")
obj = Child()
obj.show()
Output:
Inside Parent
Inside Child
super() over explicit class namesUsing Parent.show(self) hardcodes the parent class name, which breaks if you rename the class or restructure the hierarchy. super() automatically follows the MRO and works correctly in complex inheritance scenarios:
# INCORRECT: Fragile behavior if Parent is renamed or hierarchy changes
Parent.show(self)
# CORRECT: Robust behavior that follows MRO automatically
super().show()
super() With Multilevel Inheritance
When super() is used across multiple levels, each class calls its parent's method in turn, creating a chain of calls that follows the MRO:
class TutRef1:
def __init__(self):
print("Initialized TutRef1")
def process(self, value):
print(f"TutRef1 processing: {value}")
class TutRef2(TutRef1):
def __init__(self):
print("Initialized TutRef2")
super().__init__()
def process(self, value):
print(f"TutRef2 processing: {value}")
super().process(value + 1)
class TutRef3(TutRef2):
def __init__(self):
print("Initialized TutRef3")
super().__init__()
def process(self, value):
print(f"TutRef3 processing: {value}")
super().process(value + 1)
obj = TutRef3()
print()
obj.process(10)
Output:
Initialized TutRef3
Initialized TutRef2
Initialized TutRef1
TutRef3 processing: 10
TutRef2 processing: 11
TutRef1 processing: 12
Each __init__() and process() call propagates up the chain via super(), ensuring all levels of the hierarchy participate.
A Practical Example: Extending Behavior
Method overriding is commonly used to specialize general behavior. Here is a practical example with a shape hierarchy:
class Shape:
def __init__(self, color="black"):
self.color = color
def area(self):
return 0
def describe(self):
return f"A {self.color} shape with area {self.area():.2f}"
class Circle(Shape):
def __init__(self, radius, color="red"):
super().__init__(color)
self.radius = radius
def area(self):
return 3.14159 * self.radius ** 2
class Rectangle(Shape):
def __init__(self, width, height, color="blue"):
super().__init__(color)
self.width = width
self.height = height
def area(self):
return self.width * self.height
# Polymorphism in action
shapes = [Circle(5), Rectangle(4, 6), Shape()]
for shape in shapes:
print(shape.describe())
Output:
A red shape with area 78.54
A blue shape with area 24.00
A black shape with area 0.00
Notice that describe() is defined only once in the Shape class, but it calls self.area(). Because area() is overridden in each subclass, the correct version is called automatically: this is polymorphism at work.
Common Mistake: Forgetting to Call super().__init__()
class Parent:
def __init__(self):
self.name = "Parent"
class Child(Parent):
def __init__(self):
# INCORRECT: Missing super().__init__() call
self.age = 10
obj = Child()
print(obj.age) # Works: 10
print(obj.name) # AttributeError: 'Child' object has no attribute 'name'
Fix
Always call super().__init__() if the parent class performs any initialization:
class Parent:
def __init__(self):
self.name = "Parent"
class Child(Parent):
def __init__(self):
super().__init__() # CORRECT: Parent's __init__ runs first
self.age = 10
obj = Child()
print(obj.age) # Works: 10
print(obj.name) # AttributeError: 'Child' object has no attribute 'name'
Output:
10
Parent
Summary
| Concept | Description |
|---|---|
| Method overriding | Child class redefines a method from the parent class |
| Which version runs? | Determined by the object's type, not the variable's type |
super() | Calls the parent class's method; follows the MRO |
| Multiple inheritance | Child can override methods from any parent |
| Multilevel inheritance | Each level in the chain can override and extend |
| MRO | Method Resolution Order: the sequence Python searches for methods |
Conclusion
Method overriding is a fundamental object-oriented programming concept that enables polymorphism and code specialization in Python.
- By defining a method in a child class with the same name as one in the parent class, you can customize behavior while maintaining a consistent interface.
- Use
super()to call the parent's implementation when you want to extend rather than replace behavior, and always callsuper().__init__()in child constructors to ensure proper initialization.
Whether you are working with single, multiple, or multilevel inheritance, method overriding gives you the flexibility to build clean, maintainable class hierarchies.