Skip to contentSkip to navigation
MODULE 09 · LESSON 04

Inheritance & Method Overriding

Extend base class capabilities, leverage super(), and implement polymorphic interfaces across subclasses.

Core Learning Milestones:
Method Overriding
Subclasses specialize behavior
super()
Dynamic proxy, not hardcoded parent
MRO
C3 Linearization resolves order
Polymorphism
isinstance/issubclass + duck typing
Estimated Time
30 Minutes
Comprehensive deep dive
Difficulty Level
Intermediate
Builds on Method Flavors
Knowledge Check
15 Questions
+35 XP Potential
01 / Foundations

Subclasses Specialize, super() Extends

When a subclass defines a method with the same name as its parent, calling that method on a subclass instance runs the subclass's version — this is method overriding. It lets each subclass specialize shared behavior without touching the parent.

super() is not a hardcoded reference to "the parent class" — it's a dynamic proxy that dispatches to the next class in the runtime Method Resolution Order (MRO), which Python computes with an algorithm called C3 Linearization.

Skipping base initialization
def __init__(self, name, salary):
  self.salary = salary # name never set!
Extending via super()
def __init__(self, name, salary):
  super().__init__(name)
  self.salary = salary
Think of it like this:

Inheritance is like a franchise inheriting the parent company's playbook: each location (subclass) can override a specific procedure (method) for local needs, but calling super() is like phoning corporate headquarters to run their standard process first, then adding your own steps on top.

Full Contract in Action

Specializing Behavior via Method Overriding

Run the snippet below to see two subclasses override the same parent method differently:

overriding.py — Press 'Run Code' to execute in your browser
class NotificationSender:
    def send(self, recipient, message):
        return f"[GENERIC] To: {recipient} | Msg: {message}"

class EmailSender(NotificationSender):
    def send(self, recipient, message):
        # Overriding base method with email-specific formatting
        return f"[EMAIL SMTP] Sending '{message}' to <{recipient}>"

class SMSSender(NotificationSender):
    def send(self, recipient, message):
        # Overriding with SMS-specific formatting
        return f"[SMS GATEWAY] Sending '{message}' to phone {recipient}"

services = [
    EmailSender(),
    SMSSender(),
    NotificationSender()
]

for s in services:
    print(s.send("user@pylabs.dev", "Welcome aboard!"))
Inheritance Glossary: Core Concepts
OverridingSpecialize

A subclass redefining a method with the same name as the parent's replaces that behavior for its instances.

super()Proxy

A dynamic proxy to the next class in the MRO — not a hardcoded reference to the immediate parent.

__mro__Order

The linear class order Python computed via C3 Linearization for attribute and method lookup.

isinstance()Polymorphism

Confirms an object shares ancestry with a class, enabling code that works across an entire subtype tree.

02 / super() & MRO Walker

Step Through Overriding, super(), and the MRO

Click through the three concepts that together make inheritance work correctly:

1. Subclass overrides
class SMSGateway(Notifier):   def send(self, msg):     return f'[SMS] {msg}'

A subclass defines a method with the same name as the parent's — calling it on the subclass instance runs this version instead of the parent's.

03 / Polymorphism Reference

Subtype Polymorphism vs Duck Typing

Both let one function handle many types — knowing which one you're relying on avoids surprises.

Subtype polymorphism

isinstance(obj, StorageEngine)

True for the base class and any subclass — guarantees a shared ancestry and interface.

issubclass(MemoryStorage, StorageEngine)

Checks the class relationship directly, without needing an instance.

Duck typing

if hasattr(obj, 'save'): obj.save()

Prioritizes behavior over ancestry — any object with the right method works, related or not.

Method resolution

ClassName.__mro__

The exact linear order Python searches for attributes and methods across the inheritance tree.

04 / Hands-on Examples

Worked Hands-on Walkthroughs

Progress step-by-step from overriding through super()-based extension to polymorphic introspection:

Walkthrough 01 · FoundationLevel 1 · Foundation

Specializing Behavior via Method Overriding

Overriding
Problem Scenario

Each subclass overrides send() with its own transport formatting while sharing the common NotificationSender interface.

Executable Solution
Specializing Behavior via Method Overriding
class NotificationSender:
    def send(self, recipient, message):
        return f"[GENERIC] To: {recipient} | Msg: {message}"

class EmailSender(NotificationSender):
    def send(self, recipient, message):
        # Overriding base method with email-specific formatting
        return f"[EMAIL SMTP] Sending '{message}' to <{recipient}>"

class SMSSender(NotificationSender):
    def send(self, recipient, message):
        # Overriding with SMS-specific formatting
        return f"[SMS GATEWAY] Sending '{message}' to phone {recipient}"

services = [
    EmailSender(),
    SMSSender(),
    NotificationSender()
]

for s in services:
    print(s.send("user@pylabs.dev", "Welcome aboard!"))
Step-by-Step Explanation

Each subclass overrides send() with its own transport formatting while sharing the common NotificationSender interface.

Walkthrough 02 · Building UpLevel 2 · Applied

Extending Parent State Using super().__init__

super()
Problem Scenario

super().__init__ sets emp_id and base_salary on the Employee base, and super().calculate_pay() computes base earnings.

Executable Solution
Extending Parent State Using super().__init__
class Employee:
    def __init__(self, emp_id, name, base_salary):
        self.emp_id = emp_id
        self.name = name
        self.base_salary = base_salary

    def calculate_pay(self):
        return self.base_salary

class Manager(Employee):
    def __init__(self, emp_id, name, base_salary, bonus):
        # 1. Delegate core initialization to Employee superclass
        super().__init__(emp_id, name, base_salary)
        # 2. Add Manager-specific state
        self.bonus = bonus

    def calculate_pay(self):
        # 3. Extend base pay calculation
        return super().calculate_pay() + self.bonus

mgr = Manager(101, "Elena Rostova", 95000, 15000)
print(f"{mgr.name} (ID #{mgr.emp_id}) total pay: ${mgr.calculate_pay()}")
Step-by-Step Explanation

super().__init__ sets emp_id and base_salary on the Employee base, and super().calculate_pay() computes base earnings.

Walkthrough 03 · MasteryLevel 3 · Architecture

Polymorphism and Introspection with isinstance & issubclass

Polymorphism
Problem Scenario

persist_record handles any StorageEngine subtype polymorphically, and isinstance() checks support tuple alternatives.

Executable Solution
Polymorphism and Introspection with isinstance & issubclass
class StorageEngine:
    def save(self, key, data): raise NotImplementedError

class MemoryStorage(StorageEngine):
    def __init__(self): self.store = {}
    def save(self, key, data): self.store[key] = data

class CloudStorage(StorageEngine):
    def save(self, key, data): print(f"Uploading {key} to S3/R2")

def persist_record(engine, key, data):
    # Verify nominal subtype polymorphism
    if not isinstance(engine, StorageEngine):
        raise TypeError(f"Expected StorageEngine, got {type(engine).__name__}")
    engine.save(key, data)

mem = MemoryStorage()
persist_record(mem, "user:1", {"name": "Kaelen"})
print("Memory store contents:", mem.store)
print("Is MemoryStorage subclass of StorageEngine?", issubclass(MemoryStorage, StorageEngine))
print("Is mem an instance of (CloudStorage, MemoryStorage)?", isinstance(mem, (CloudStorage, MemoryStorage)))
Step-by-Step Explanation

persist_record handles any StorageEngine subtype polymorphically, and isinstance() checks support tuple alternatives.

05 / Traps to Avoid

Common Traps & How to Avoid Them

Study the mistakes and clean fixes below before building your own class hierarchies:

!Trap 1: Syntax or Name Typos in Inheritance & Polymorphism
Common Trap
The Bug (Avoid)Buggy Code
# Mistyped variable or missing closing parenthesis result = inheritance___polymorphism_val
What Happens:
NameError: name is not defined
The Clean FixCorrected Code
# Declare and verify variables before execution val = 10 print("Defined correctly:", val)
Recommended Practice:
Always call super().__init__() in a subclass constructor to preserve base-class invariants.
Why It Happens

Beginners frequently encounter typos or forgotten definitions before using an identifier.

!Trap 2: Unintended State Mutation or Scope Leakage
Subtle Trap
The Bug (Avoid)Buggy Code
# Modifying shared state or unexpected variable shadowing def update_state(data=[]): data.append(1) return data
What Happens:
Default mutable argument accumulates values across separate function calls.
The Clean FixCorrected Code
def update_state(data=None): if data is None: data = [] data.append(1) return data
Recommended Practice:
Always call super().__init__() in a subclass constructor to preserve base-class invariants.
Why It Happens

Default arguments in Python are evaluated once at function definition time, not at invocation time.

!Trap 3: Resource Leaks & High-Scale Performance Regressions
Architecture Trap
The Bug (Avoid)Buggy Code
# Opening resources in a tight loop without closing or bounding memory files = [open(f"log_{i}.txt", "w") for i in range(5000)]
What Happens:
OSError: [Errno 24] Too many open files (file descriptor exhaustion)
The Clean FixCorrected Code
for i in range(5): with open(f"log_{i}.txt", "w") as f: f.write("Safe")
Recommended Practice:
Always call super().__init__() in a subclass constructor to preserve base-class invariants.
Why It Happens

Failing to release system resources leads to process termination under heavy production workloads.

06 / Real-World Systems

Real-World Systems in Action

See how scripts, backend services, and enterprise codebases build on inheritance and polymorphism:

Developer ProductivityFoundation Script

Local Developer Automation Tool

SYSTEM #1
Organization Context

Individual developers and IT administrators automate everyday repetitive workflows.

Why This Pattern Works Here

A clean standalone script providing fast, local automation with zero dependencies.

Production Code Wrapper
Local Developer Automation Tool
class Vehicle:
    def move(self):
        return "Moving forward"

class Airplane(Vehicle):
    def move(self):
        return "Flying through the sky"

plane = Airplane()
print("Plane movement:", plane.move())
Cloud MicroservicesScaled Service · Applied

Backend API Service Integration

SYSTEM #2
Organization Context

SaaS platforms run backend microservices processing requests concurrently.

Why This Pattern Works Here

A modular service handler integrating validation and structured responses.

Production Code Wrapper
Backend API Service Integration
# Service integration layer
def handle_request(payload: dict) -> dict:
    return {"status": "success", "processed": True}
Distributed SystemsEnterprise · Architecture

High-Throughput Cloud Architecture

SYSTEM #3
Organization Context

GUI toolkits and HTTP client middleware use polymorphism so callers treat all request handlers identically regardless of subtype.

Why This Pattern Works Here

GUI toolkits and HTTP client middleware use polymorphism so callers treat all request handlers identically regardless of subtype.

Production Code Wrapper
High-Throughput Cloud Architecture
# High-resilience production pattern
import time

def process_stream(events: list) -> int:
    processed = 0
    for event in events:
        if event:
            processed += 1
    return processed
Key Takeaways

Key Points & Concept Review

  • Method overriding allows subclasses to tailor parent behavior to specialized requirements.
  • super() is a dynamic proxy that dispatches method calls to the next class in the runtime MRO.
  • Always use super().__init__() in subclasses to ensure base class state and invariants are initialized.
  • Python resolves multiple inheritance using the C3 Linearization algorithm accessible via __mro__.
  • Duck typing prioritizes object behavior over nominal class pedigree, while subtype polymorphism guarantees shared ancestry.
  • isinstance(obj, tuple) and issubclass(cls, tuple) provide runtime validation across inheritance trees.
Study Guide & References

Frequently Asked Questions & Reference Hub

FAQ & References
Growth Mindset: The MRO Clicks Once You Print It

super() feels abstract until you actually print ClassName.__mro__ for a hierarchy you wrote yourself — it's completely fine if multiple inheritance order confuses you at first; most working Python developers rarely go past two or three levels deep.

Related Built-in References Right Here
PyLabs Library Docs
Ready for Action

You've Mastered Inheritance & Polymorphism!

You understand overriding, super(), the MRO, and polymorphic code. Now lock in your knowledge with hands-on practice.

Hands-on Code Challenge

Build a small subclass hierarchy that overrides and extends via super().

+40 XP Reward

Earn experience points toward your developer rank as soon as you pass.

~3 Minutes to Complete

Bite-sized knowledge check with instant explanations for every question.

Worked example

Every vehicle honks, but buses charge fare. Bus inherits Vehicle and adds its own twist — code reuse plus specialisation.

Example
class Vehicle:
    def describe(self):
        return "A vehicle"

class Bus(Vehicle):
    def fare(self, km):
        return km * 2.5

bus = Bus()
print(bus.describe(), bus.fare(10))

How it works, step by step

  1. class Bus(Vehicle) means Bus gets everything Vehicle has.
  2. describe() was never written twice — inheritance supplied it.
  3. fare() exists only on Bus: specialisation.

Your notes

Sign in to keep private notes alongside this lesson.

Sign in to take notes

Inheritance & Method Overriding practice

Sign-in required

Create a parent class Vehicle with a move method that prints Moving. Create a child class Bike that overrides it to print Pedaling. Create a Bike and call move().

Coding Practice is Locked

Sign in to access the interactive Python editor, write and run code checks against automated tests, use hints, and earn XP.

Practice quiz

Check yourself

15 questions with explanations after submission, so you can correct course as you go.

Inheritance & Method Overriding quiz

Locked

You must be signed in to take this quiz. Sign in to test your understanding, track your answers, and earn XP.

Lesson incomplete

Not completed

Create an ID or sign in to save lesson completion across devices.

Create ID to save
Module lesson progress0/6 complete
Module cheat sheet