Reusing Code With Inheritance
Inheritance lets one class reuse and extend another. Instead of copy-pasting common code, you say 'this is a kind of that' and let Python wire up the methods automatically. This chapter covers single, multiple, multi-level, and hierarchical inheritance, the powerful super() call, and the MRO that determines who answers a method call.
Overview
Code Reuse
Avoid duplicating logic — define it once in the parent.
Extensible
Subclasses can add or refine behaviour without touching the parent.
Override Friendly
Customise specific methods while inheriting the rest.
Hierarchies
Model real-world classifications like Vehicle → Car → ElectricCar.
MRO Clarity
Python's deterministic lookup order keeps multiple inheritance predictable.
Syntax
- Subclass:
class Child(Parent):. - Multiple inheritance:
class Z(A, B):. - Call parent methods with
super().method(). - Override a method by redefining it in the subclass.
class Animal:
def __init__(self, name):
self.name = name
def speak(self):
return "generic sound"
class Dog(Animal):
def speak(self):
return f"{self.name} says Woof!"
class Puppy(Dog):
def speak(self):
return f"{self.name} says yip!"
print(Puppy("Bruno").speak())
Detailed Explanation
- Single inheritance: One subclass, one parent. The simplest and most common form. Example:
class Manager(Employee). - Multi-level inheritance: A chain:
Grandparent → Parent → Child. Each level adds or refines behaviour. - Multiple inheritance: A class with two or more parents. Use carefully — Python's MRO (Method Resolution Order) determines which parent's method wins.
- Hierarchical inheritance: One parent, many subclasses (e.g.
Shape → Square, Circle, Triangle). - super(): Calls the next class in the MRO. Use it to invoke parent constructors or extend parent methods cleanly.
- MRO: Python uses C3 linearisation to compute method lookup order. Access via
ClassName.__mro__orClassName.mro().
Code Examples
class Animal:
def __init__(self, name):
self.name = name
def speak(self):
return "?"
class Cat(Animal):
def speak(self):
return f"{self.name} says Meow"
print(Cat("Mittens").speak())
class A:
def hello(self): return "A"
class B(A):
def hello(self): return "B" + super().hello()
class C(B):
def hello(self): return "C" + super().hello()
print(C().hello())
class Walker:
def move(self): return "walking"
class Swimmer:
def move(self): return "swimming"
class Duck(Walker, Swimmer):
pass
print(Duck().move())
print(Duck.__mro__)
(<class 'Duck'>, <class 'Walker'>, <class 'Swimmer'>, <class 'object'>)
class Shape:
def area(self): return 0
class Square(Shape):
def __init__(self, s): self.s = s
def area(self): return self.s ** 2
class Circle(Shape):
def __init__(self, r): self.r = r
def area(self): return 3.14 * self.r ** 2
for sh in [Square(4), Circle(3)]:
print(sh.area())
28.26
class Employee:
def __init__(self, name, salary):
self.name = name
self.salary = salary
class Manager(Employee):
def __init__(self, name, salary, team):
super().__init__(name, salary)
self.team = team
m = Manager("Riya", 80000, "Backend")
print(m.name, m.team)
class Animal: pass class Dog(Animal): pass d = Dog() print(isinstance(d, Dog)) print(isinstance(d, Animal)) print(issubclass(Dog, Animal))
True
True
Real-World Use Cases
Taxonomies
Biological hierarchies — Animal → Mammal → Dog.
GUI Toolkits
Widget → Button, Widget → Slider, Widget → Label.
ORM Models
Django's Model base class is inherited by every table model.
Vehicles
Vehicle → Car → ElectricCar, sharing common engine/wheels behaviour.
Exception Hierarchies
ValueError and TypeError inherit from Exception.
Mixins
Reuse small features (e.g. LoggableMixin, JsonMixin) across many classes.
Notes & Pro Tips
- Use inheritance for is-a relationships, composition for has-a.
- Avoid deep hierarchies (more than 3 levels) — they get hard to follow.
- Always call
super().__init__()in subclass constructors. - Prefer mixins or composition over multiple inheritance when possible.
- Use
isinstance(obj, ClassOrTuple)rather thantype(obj) == Class— it respects inheritance. - Check the MRO with
Class.mro()when methods don't behave as expected.
Common Mistakes
- Diamond ambiguity: in multiple inheritance Python uses MRO; learn it to avoid surprises.
- Forgetting
super(): skipping parent init leaves attributes unset. - Overriding without intent: accidentally hiding parent behaviour.
- Tightly coupled hierarchies: changing the parent breaks many subclasses.
- Misusing inheritance for code reuse: when there's no 'is-a', composition is better.
- Using
type()for checks: doesn't recognise subclasses; preferisinstance.
Practice Problems
- Problem 1: Model a base
Vehicleand subclassesCarandBike. - Problem 2: Create
Animal→Bird→Eaglewith overriddenspeak()methods. - Problem 3: Demonstrate multiple inheritance with a
SwimmerandFlyermixed intoDuck. - Problem 4: Implement an
Employeebase class and subclassesManagerandDeveloperwith overriddenbonus(). - Problem 5: Create a
Shapehierarchy witharea()implemented differently for square, circle, triangle. - Problem 6: Build a logging mixin and use it with two unrelated classes.
Interview Questions
- Q1. What is inheritance?
- Q2. What is the difference between single, multi-level and multiple inheritance?
- Q3. What is the MRO and how does Python compute it?
- Q4. When should you use inheritance vs composition?
- Q5. Why must you call
super().__init__()in subclasses? - Q6. Why is multiple inheritance considered risky?
Frequently Asked Questions
- Q1: Does Python support multiple inheritance?
Yes. A class can inherit from more than one parent, and Python uses the C3 MRO to determine method lookup order. - Q2: What is the MRO?
The Method Resolution Order — the deterministic sequence Python uses to look up attributes and methods through the inheritance chain. View it with ClassName.__mro__. - Q3: What is the diamond problem?
When two parents share a common grandparent, calling super() can revisit the grandparent multiple times. Python's MRO solves it by linearising the hierarchy. - Q4: Is composition better than inheritance?
Often, yes — composition is more flexible and less coupled. Use inheritance only when there's a true 'is-a' relationship. - Q5: Can a class inherit from itself?
No — that would be infinite recursion. Python forbids it at class-creation time. - Q6: How do I check if X is a subclass of Y?
Use issubclass(X, Y). For instance checks, use isinstance(obj, Y).
Summary
Inheritance is one of OOP's most powerful tools — done right, it eliminates duplication and creates clear taxonomies. Use it for true is-a relationships, keep hierarchies shallow, prefer composition or mixins when behaviour varies orthogonally, and let super() and the MRO do the heavy lifting. Then your class design will scale gracefully as your codebase grows.
Continue Learning
Previous
Go to Previous Chapter