Phase 3 · Object-Oriented PythonModule 15~40 min read

Inheritance & Polymorphism

Reuse and extend behavior with inheritance, super(), MRO, and duck typing.

What you'll learn

Real systems are full of things that are kinds of other things. Inheritance lets classes build on one another, and polymorphism lets you treat many types through one interface. Python adds its own twist: duck typing.

By the end you'll be able to:

  • Inherit from a parent class and call it with super()
  • Override inherited methods
  • Use polymorphism to write flexible code
  • Understand duck typing and abstract base classes

Inheritance & super()

A subclass inherits the attributes and methods of its parent — you write the parent in parentheses: class Dog(Animal). Inside, super() refers to the parent, letting you reuse its __init__ and methods rather than repeating them:

An inheritance hierarchy

Animal

name · speak()

Dog · Woof
Cat · Meow
Cow · Moo

Each subclass inherits name & speak(), then specialises it.

inheritance.py
class Animal:
    def __init__(self, name):
        self.name = name

    def speak(self):
        return f"{self.name} makes a sound"

class Dog(Animal):                    # Dog inherits from Animal
    def __init__(self, name, breed):
        super().__init__(name)        # call the parent's __init__
        self.breed = breed

    def speak(self):                  # override the method
        return f"{self.name} barks"

d = Dog("Rex", "Labrador")
print(d.speak())        # Rex barks
print(d.breed)          # Labrador (its own attribute)

Overriding

A subclass can replace an inherited method by defining one with the same name — overriding it. In Dog above, speak() replaces Animal.speak(). Python doesn't need any special annotation; the subclass's version simply wins.

Polymorphism

Here's the payoff. A collection of different objects can be treated uniformly, and each responds in its own way. One loop, one method call — many behaviours:

polymorphism.py
class Cat(Animal):
    def speak(self):
        return f"{self.name} meows"

animals = [Dog("Rex", "Lab"), Cat("Milo"), Animal("Thing")]

for a in animals:       # all treated as Animal...
    print(a.speak())    # ...but each runs its OWN speak()

Duck typing

Python takes polymorphism further with duck typing: "if it walks like a duck and quacks like a duck, it's a duck." Python doesn't care about an object's type — only whether it has the method or attribute you need. This makes code remarkably flexible:

ducks.py
class Duck:
    def quack(self): return "Quack!"

class Person:
    def quack(self): return "I'm imitating a duck!"

def make_it_quack(thing):
    # we don't check the TYPE — just that it can quack
    print(thing.quack())

make_it_quack(Duck())
make_it_quack(Person())

Key idea

In many languages, make_it_quack would require both classes to share a common interface. In Python, they just need a quack() method. This "ask forgiveness, not permission" style is deeply Pythonic — you use the object and let it work if it can.

Abstract base classes

Sometimes you do want to guarantee that subclasses provide certain methods. An abstract base class (from the abc module) can't be instantiated and can declare @abstractmethods that every subclass is forced to implement:

shapes.py
from abc import ABC, abstractmethod

class Shape(ABC):                     # an abstract base class
    @abstractmethod
    def area(self):                   # subclasses MUST implement this
        ...

class Circle(Shape):
    def __init__(self, r): self.r = r
    def area(self): return 3.14159 * self.r ** 2

# Shape()      # TypeError — can't instantiate an abstract class
print(round(Circle(2).area(), 2))

Tip

Prefer composition over inheritance when a relationship is "has-a" rather than "is-a" — a Car has an Engine, so store one as an attribute rather than inheriting. Deep inheritance trees get fragile fast.

Recap & quick check

Key takeaways

  • A subclass inherits from a parent: class Dog(Animal); super() calls the parent's methods.
  • Override a method by redefining it in the subclass — no annotation needed.
  • Polymorphism: different objects respond to the same method call in their own way.
  • Duck typing: Python cares that an object HAS the method, not what type it is.
  • Abstract base classes (abc) force subclasses to implement @abstractmethods; favour composition over deep inheritance.

Quick check

1. How does a class inherit from another in Python?

2. What does super() do?

3. What is duck typing?

4. Can you create an object of an abstract base class?

5. When should you prefer composition over inheritance?

Excellent — you can now model rich hierarchies. Next up: Module 16 — Magic (Dunder) Methods.