Inheritance

Create extensible class hierarchies, reuse behavior safely, and design APIs that stay flexible as your codebase grows.

Part of the free Python course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.

What You'll Learn in This Lesson

1️⃣ Why Inheritance? Why Polymorphism?

🏠 Real-World Analogy:

Think of a family tree . Children inherit traits from their parents (eye color, height) but can also have their own unique traits. In programming, a child class inherits features from a parent class but can add or change behaviors.

Concept

What It Means

Example

Inheritance

A child class reuses and extends a parent class

Dog inherits from Animal

Polymorphism

"Many forms" - same method name, different behaviors

.speak() returns "Woof!" or "Meow!"

Polymorphism means "many forms": different classes expose the same method name but implement it differently, so your high-level code doesn't care which concrete type it's using—only that it supports the interface.

💡 Why This Matters: This improves extensibility , testability , and readability in real projects like games, APIs, and data pipelines.

2️⃣ Core Inheritance Syntax

📝 The Basic Pattern:

✨ Key Point: The child inherits ALL attributes and methods from the parent. You can then override (replace) or extend (add to) them.

3️⃣ super() and Constructor Chaining

🤔 What is super()?

super() is a special function that gives you access to the parent class . It's like saying "Hey, parent class, please run YOUR version of this method first!"

Without super()

With super()

Parent's __init__ is NOT called

Parent's __init__ IS called ✅

Parent's attributes are missing

All parent attributes are set up properly ✅

4️⃣ Method Overriding & Extension Patterns

Override to change behavior; extend to add extra steps:

5️⃣ Multiple Inheritance & MRO (Method Resolution Order)

A smartphone inherits features from both a phone AND a camera . That's multiple inheritance—one class getting abilities from multiple parents!

Term

Multiple Inheritance

A class inherits from 2+ parent classes

MRO

Method Resolution Order—the order Python checks classes for a method

6️⃣ When to Use Inheritance (and When Not)

✅ Use when:

❌ Avoid when:

Rule of thumb: Favor composition over inheritance unless the IS-A relation is obvious and stable.

7️⃣ Polymorphism via Duck Typing

🦆 What is Duck Typing?

"If it walks like a duck and quacks like a duck, it's a duck!"

In Python, we don't care what type an object is—we only care what it can do . If it has a .speak() method, we can call it!

Traditional OOP

Python Duck Typing

Must inherit from same parent

No inheritance required! ✅

Strict type checking

Just needs the right methods ✅

✨ Key Insight: This is duck typing in action! Python is flexible—it doesn't need formal interfaces or shared parents.

8️⃣ Abstract Base Classes (ABCs) for Robust APIs

🤔 What is an Abstract Class?

An abstract class is like a template or contract . It says "any class that inherits from me MUST implement these methods" but doesn't provide the implementation itself.

Regular Class

Abstract Class

Can be instantiated directly

Cannot be instantiated ❌

All methods have implementations

Some methods are just "promises"

Child classes can override optionally

Child classes MUST implement abstract methods

9️⃣ Protocols (typing) for Structural Polymorphism

With type hints you can express "any object with .area() is acceptable"—even if it doesn't inherit from Shape.

This is powerful for plug-in systems and testing.

🔟 Liskov Substitution Principle (LSP)

🏠 Simple Explanation:

If you have code that works with Animal , it should also work with Dog or Cat without any surprises. A child class should behave like its parent, not break expectations.

✅ Good (Follows LSP)

❌ Bad (Violates LSP)

Bird.fly() → flies as expected

Penguin.fly() → raises error!

Rectangle.area() → returns area

Square.area() → changes width when you set height?!

⚠️ Signs You're Violating LSP:

💡 Rule of Thumb: If you need to check "is this a Dog or a Cat?" before calling a method, you might be violating LSP. Good polymorphism means you don't need to check!

1️⃣1️⃣ Composition vs Inheritance — Concrete Example

Composition is easier to refactor and avoids MRO tangles.

1️⃣2️⃣ Mixins: Small, Focused Behavior Blocks

A mixin is a parent with only helper behavior, no standalone identity.

Use mixins sparingly, name them *Mixin , and avoid shared state.

1️⃣3️⃣ Real-World Hierarchy: Shapes & Polymorphic Area

1️⃣4️⃣ Overriding Pitfalls & Best Practices

1️⃣5️⃣ Polymorphism Beyond Methods: Operators & Special Methods

Special methods let your classes participate in Python's operators—another form of polymorphism.

1️⃣6️⃣ Testing Polymorphism (Strategy Pattern Feel)

No inheritance required, but you still get clean polymorphism. If you later need guarantees, move to an ABC/Protocol.

1️⃣7️⃣ Multiple Inheritance Done Right (Cooperative super())

All classes call super() with **kw , so init flows through the MRO smoothly.

1️⃣8️⃣ Performance & Practicality

🎯 1️⃣9️⃣ Practice Challenge (with Guided Solution)

Task:

📋 Quick Reference — Inheritance

class Dog(Animal):

Inherit from Animal

super().__init__(name)

Call parent constructor

def speak(self):

Override a parent method

Dog.mro()

See Method Resolution Order

from abc import ABC, abstractmethod

Abstract base class

You now understand inheritance, method overriding, super(), MRO, and polymorphism — the tools that make Python class hierarchies clean and extensible.

Up next: Decorators & Advanced Features — learn to wrap functions with reusable behaviour using Python's elegant decorator syntax.

Practice quiz

How do you make class Dog inherit from class Animal?

  • class Dog -> Animal:
  • class Dog inherits Animal:
  • class Dog(Animal):
  • class Dog extends Animal:

Answer: class Dog(Animal):. Python uses parentheses: class Dog(Animal): puts the parent class inside the parentheses.

Inside a child's __init__, how do you call the parent class's __init__?

  • super().__init__(name)
  • parent.__init__(name)
  • Animal.init(name)
  • self.super(name)

Answer: super().__init__(name). super().__init__(name) runs the parent constructor so its setup (like self.name) actually happens.

What does Duck.mro() return for class Duck(Walker, Swimmer)?

  • Walker
  • Swimmer
  • Duck

The MRO lists Duck first, then parents left-to-right (Walker, Swimmer), then object.

What does the @abstractmethod decorator (from abc) enforce?

  • The method runs automatically
  • Child classes must implement that method
  • The method becomes static
  • The method is cached

Answer: Child classes must implement that method. An abstractmethod is a promise: any concrete subclass must implement it, or instantiation fails.

What is 'duck typing' in Python?

  • Caring only about whether an object has the needed method, not its type
  • Requiring all objects to share a parent class
  • A way to copy ducks
  • Strict compile-time type checking

Answer: Caring only about whether an object has the needed method, not its type. Duck typing: if it has .speak(), you can call it — Python checks behavior, not the exact type.

Given Vector2 with __add__ defined, what does Vector2(1, 2) + Vector2(3, 4) produce?

  • Vector2(3, 8)
  • Vector2(1, 2, 3, 4)
  • Vector2(4, 6)
  • a TypeError

Answer: Vector2(4, 6). __add__ adds component-wise: x=1+3=4, y=2+4=6, giving Vector2(4, 6).

An abstract base class created with abc.ABC and an abstractmethod...

  • can be instantiated directly
  • cannot be instantiated directly
  • has no methods
  • must inherit from object explicitly

Answer: cannot be instantiated directly. Abstract classes act as templates/contracts and cannot be instantiated directly.

Which relationship signals you should prefer composition over inheritance?

  • IS-A (Dog is an Animal)
  • Any 2-level hierarchy
  • Polymorphic methods
  • HAS-A (Car has an Engine)

Answer: HAS-A (Car has an Engine). A HAS-A relationship (a Car has an Engine) is best modeled with composition, not inheritance.

The Liskov Substitution Principle (LSP) says a subclass should...

  • always add new attributes
  • be usable anywhere its parent is expected, without surprises
  • never override methods
  • require stricter inputs than the parent

Answer: be usable anywhere its parent is expected, without surprises. LSP: code that works with the parent type should work with any subclass without breaking expectations.

What is a mixin?

  • A class that cannot be subclassed
  • A function decorator
  • A small parent that adds focused behavior, with no standalone identity
  • A way to merge two instances

Answer: A small parent that adds focused behavior, with no standalone identity. A mixin is a small, behavior-only parent (named *Mixin) you mix in to add reusable functionality.

Continue this course