Technology  /  Python

๐Ÿ Python 78 guides ยท updated 2026

From first variable to OOP, generators, and real projects โ€” the language that runs everything from data pipelines to AI agents, taught the practical way.

Method Overriding and super() in Python: Extending Without Replacing

When a subclass defines a method with the same name as a method in its parent class, the subclass version takes precedence. That is method overriding. When you want the subclass to extend the parentโ€™s behaviour rather than replace it entirely, you use super() to call back into the parent. Together, these two tools give subclasses precise control over inherited behaviour.

Method Overriding: The Core Idea

class Notification:
def send(self, message):
print(f"[Notification] {message}")
class EmailNotification(Notification):
def send(self, message):
print(f"[Email] Sending: {message}")
class SMSNotification(Notification):
def send(self, message):
print(f"[SMS] Sending: {message}")
# Each subclass decides what "send" means for it
notifications = [EmailNotification(), SMSNotification(), Notification()]
for n in notifications:
n.send("Your order has shipped")

Output:

[Email] Sending: Your order has shipped
[SMS] Sending: Your order has shipped
[Notification] Your order has shipped

Python looks up the method on the objectโ€™s actual class first. If it finds a send method there, it calls that one โ€” the parentโ€™s version is not called automatically.

super(): Extending Rather Than Replacing

Most of the time, you do not want to throw away the parentโ€™s logic โ€” you want to add to it. super() returns a proxy that delegates to the next class in the method resolution order.

class Logger:
def log(self, message):
print(f"[LOG] {message}")
class TimestampLogger(Logger):
def log(self, message):
from datetime import datetime
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
# Call the parent first, then add our own line
super().log(message)
print(f" Logged at: {timestamp}")
tl = TimestampLogger()
tl.log("Payment processed")
# [LOG] Payment processed
# Logged at: 2025-06-14 09:30:00

You can call super() before or after your own code depending on what makes sense:

super().__init__(): Initialising the Parent

The most common use of super() is inside __init__(). When a subclass has its own attributes, it must still initialise the parentโ€™s attributes.

class Shape:
def __init__(self, colour, filled):
self.colour = colour
self.filled = filled
def describe(self):
fill_str = "filled" if self.filled else "outline"
return f"{fill_str} {self.colour} shape"
class Circle(Shape):
def __init__(self, colour, filled, radius):
super().__init__(colour, filled) # parent gets colour and filled
self.radius = radius # circle adds radius
def area(self):
import math
return math.pi * self.radius ** 2
def describe(self):
base = super().describe()
return f"{base} (circle, r={self.radius})"
c = Circle("blue", True, 5)
print(c.describe()) # filled blue shape (circle, r=5)
print(f"Area: {c.area():.2f}") # Area: 78.54
print(c.colour) # blue โ€” set by Shape.__init__

Without super().__init__(colour, filled), c.colour and c.filled would not exist. Any method inherited from Shape that uses them would crash with an AttributeError.

Override Patterns in Practice

Pattern 1: Complete replacement

Use when the subclass behaviour has nothing in common with the parent.

class Animal:
def sound(self):
return "..."
class Dog(Animal):
def sound(self):
return "Woof" # completely replaces parent
class Cat(Animal):
def sound(self):
return "Meow" # completely replaces parent

Pattern 2: Extension (add before)

Use when the subclass needs to do something before the parentโ€™s logic runs.

class DataProcessor:
def process(self, data):
print(f"Processing {len(data)} records")
return [item.strip() for item in data]
class ValidatingProcessor(DataProcessor):
def process(self, data):
# Validate before processing
if not data:
raise ValueError("Cannot process empty dataset")
return super().process(data) # then hand off to parent

Pattern 3: Wrapping (before and after)

Use for cross-cutting concerns like timing, logging, or transactions.

class DatabaseQuery:
def execute(self, sql):
print(f"Executing: {sql}")
return [] # placeholder
class TimedQuery(DatabaseQuery):
def execute(self, sql):
import time
start = time.perf_counter()
result = super().execute(sql)
elapsed = time.perf_counter() - start
print(f"Query took {elapsed:.4f}s")
return result
tq = TimedQuery()
tq.execute("SELECT * FROM orders WHERE status = 'pending'")

super() in Multiple Inheritance

super() is designed to work with the full method resolution order (MRO), not just one parent. This makes it essential in cooperative multiple inheritance.

class A:
def action(self):
print("A.action")
class B(A):
def action(self):
print("B.action")
super().action() # continues to C in MRO, not directly to A
class C(A):
def action(self):
print("C.action")
super().action()
class D(B, C):
def action(self):
print("D.action")
super().action()
D().action()
# D.action
# B.action
# C.action
# A.action

Pythonโ€™s MRO for D is D โ†’ B โ†’ C โ†’ A. Each class calls super(), so the chain runs completely. If B did not call super(), C.action and A.action would never run.

Common Mistakes

Calling super() with the wrong arguments (Python 2 style). In Python 3, super() with no arguments works correctly inside a method. The Python 2 form super(ClassName, self) is no longer needed and is error-prone.

# Python 2 style โ€” unnecessary in Python 3
super(Dog, self).__init__(name)
# Python 3 style โ€” just works
super().__init__(name)

Forgetting super() breaks the MRO chain. In multiple inheritance, if any class in the chain fails to call super(), the classes further up the chain do not run. This is a silent bug โ€” no error, just missing behaviour.

Calling the wrong parent directly. Writing ParentClass.method(self) instead of super().method() bypasses Pythonโ€™s MRO. In single inheritance it looks fine, but in multiple inheritance it causes methods to run twice or not at all.

Using super() outside a class. super() with no arguments only works inside a class definition. Outside, it raises a RuntimeError.