⚡IntermediateObject-Oriented Python
Inheritance and Polymorphism
Building class hierarchies with inheritance, super(), and polymorphic behaviour.
Overview
Inheritance lets you create a new class (child/subclass) that automatically gets all the attributes and methods of an existing class (parent/superclass). You extend or override only what's different, avoiding repetition.
Polymorphism means different classes can implement the same method name, and the correct version is called automatically based on the actual type of the object. Code that calls `animal.speak()` works whether `animal` is a Dog, Cat, or Bird each class provides its own `speak()` implementation.
`super()` gives you access to the parent class, most commonly used in `__init__` to run the parent's constructor before adding child-specific setup: `super().__init__(...)`.
Python also supports multiple inheritance a class can inherit from more than one parent. Python uses the C3 linearisation algorithm (MRO Method Resolution Order) to determine which parent's method is called when there's ambiguity.
Key Points
- •`class Child(Parent):` Child inherits all attributes and methods from Parent
- •`super().__init__(...)` call the parent constructor from the child
- •Override a method by defining it with the same name in the child class
- •`isinstance(obj, Class)` True if obj is an instance of Class or any subclass
- •`issubclass(Child, Parent)` True if Child inherits from Parent
- •Polymorphism: the same method name behaves differently depending on the object's type
- •MRO (`ClassName.__mro__`) shows the order Python searches for methods
Examples
Basic inheritance with super()
class Animal:
def __init__(self, name, sound):
self.name = name
self.sound = sound
def speak(self):
return f"{self.name} says {self.sound}!"
def __str__(self):
return f"{self.__class__.__name__}({self.name})"
class Dog(Animal):
def __init__(self, name):
super().__init__(name, "Woof") # reuse parent __init__
def fetch(self):
return f"{self.name} fetches the ball!"
class Cat(Animal):
def __init__(self, name):
super().__init__(name, "Meow")
rex = Dog("Rex")
whiskers = Cat("Whiskers")
print(rex.speak()) # Rex says Woof!
print(whiskers.speak()) # Whiskers says Meow!
print(rex.fetch()) # Rex fetches the ball!Polymorphism same interface, different behaviour
class Shape:
def area(self):
raise NotImplementedError("Subclass must implement area()")
class Rectangle(Shape):
def __init__(self, w, h):
self.w, self.h = w, h
def area(self):
return self.w * self.h
class Circle(Shape):
def __init__(self, r):
self.r = r
def area(self):
return 3.14159 * self.r ** 2
shapes = [Rectangle(4, 5), Circle(3), Rectangle(2, 8)]
for shape in shapes:
print(f"{shape.__class__.__name__}: area = {shape.area():.2f}")This is the power of polymorphism the loop doesn't need to know the type of each shape.
isinstance() and method resolution
class Vehicle:
def move(self): return "moving"
class Car(Vehicle):
def move(self): return "driving"
class ElectricCar(Car):
def move(self): return "driving silently"
tesla = ElectricCar()
print(isinstance(tesla, ElectricCar)) # True
print(isinstance(tesla, Car)) # True
print(isinstance(tesla, Vehicle)) # True
print(tesla.move()) # driving silently
print(ElectricCar.__mro__) # shows resolution orderCommon Mistakes
- Forgetting to call super().__init__() in the child parent attributes won't be set up
- Overriding a method but accidentally changing its signature breaks code that expects the parent interface
- Diamond inheritance without understanding MRO use ClassName.__mro__ to debug resolution order
Note
Prefer composition over inheritance when the relationship is "has-a" rather than "is-a". A Car "has-a" Engine (composition) is cleaner than a Car "is-a" Engine (inheritance). Reserve inheritance for genuine "is-a" relationships.
Exercises
Write Python code to solve each exercise. Run it in your browser and check your output.
Loading editor…
Completed: 0 / 5 exercises
Knowledge Check
Question 1 of 4