Skip to main content

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.

Why call 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.

Understanding Python's Method Resolution Order (MRO)

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
note

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.

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
Prefer super() over explicit class names

Using 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

ConceptDescription
Method overridingChild 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 inheritanceChild can override methods from any parent
Multilevel inheritanceEach level in the chain can override and extend
MROMethod 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 call super().__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.