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:
Animal
name · speak()
Each subclass inherits name & speak(), then specialises it.
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:
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:
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
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:
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
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.