Inheritance & Polymorphism
📖 Concept
Python supports single inheritance, multiple inheritance, and abstract base classes. The Method Resolution Order (MRO) determines which method gets called in a class hierarchy.
Inheritance types:
- Single inheritance:
class Child(Parent)— one parent class - Multiple inheritance:
class Child(Parent1, Parent2)— multiple parents - Multilevel inheritance:
class C(B),class B(A)— chain of parents
The super() function:
super() calls methods from the parent class following the MRO. It's essential for cooperative multiple inheritance — each class calls super() to ensure all parents' methods run.
Method Resolution Order (MRO):
Python uses the C3 linearization algorithm to determine the order in which classes are searched for methods. You can inspect it with ClassName.__mro__ or ClassName.mro().
Abstract Base Classes (ABCs):
ABCs define interfaces — they declare methods that subclasses MUST implement. Use the abc module:
from abc import ABC, abstractmethod
class Shape(ABC):
@abstractmethod
def area(self): pass
You cannot instantiate an ABC directly — subclasses must implement all abstract methods.
Duck typing is Python's preferred polymorphism style: "If it walks like a duck and quacks like a duck, it's a duck." Instead of checking types, check for capabilities (methods/attributes).
💻 Code Example
1# ============================================================2# Single inheritance3# ============================================================4class Animal:5 def __init__(self, name: str, sound: str):6 self.name = name7 self.sound = sound89 def speak(self) -> str:10 return f"{self.name} says {self.sound}!"1112 def __str__(self) -> str:13 return f"{self.__class__.__name__}({self.name})"1415class Dog(Animal):16 def __init__(self, name: str, breed: str):17 super().__init__(name, "Woof") # Call parent's __init__18 self.breed = breed1920 def fetch(self, item: str) -> str:21 return f"{self.name} fetches the {item}!"2223class Cat(Animal):24 def __init__(self, name: str):25 super().__init__(name, "Meow")2627 def speak(self) -> str:28 # Override parent method29 return f"{self.name} purrs softly... then says {self.sound}!"3031# Polymorphism — same interface, different behavior32animals = [Dog("Rex", "German Shepherd"), Cat("Whiskers")]33for animal in animals:34 print(animal.speak())35 # Rex says Woof!36 # Whiskers purrs softly... then says Meow!3738# isinstance and issubclass39print(isinstance(animals[0], Dog)) # True40print(isinstance(animals[0], Animal)) # True (inheritance chain)41print(issubclass(Dog, Animal)) # True4243# ============================================================44# Multiple inheritance and MRO45# ============================================================46class Flyable:47 def fly(self):48 return f"{self.name} is flying!"4950class Swimmable:51 def swim(self):52 return f"{self.name} is swimming!"5354class Duck(Animal, Flyable, Swimmable):55 def __init__(self, name):56 super().__init__(name, "Quack")5758donald = Duck("Donald")59print(donald.speak()) # Donald says Quack!60print(donald.fly()) # Donald is flying!61print(donald.swim()) # Donald is swimming!6263# Inspect MRO64print(Duck.__mro__)65# (Duck, Animal, Flyable, Swimmable, object)6667# ============================================================68# The diamond problem and super()69# ============================================================70class Base:71 def __init__(self):72 print("Base.__init__")7374class Left(Base):75 def __init__(self):76 print("Left.__init__")77 super().__init__() # Calls Right.__init__ (not Base!)7879class Right(Base):80 def __init__(self):81 print("Right.__init__")82 super().__init__() # Calls Base.__init__8384class Diamond(Left, Right):85 def __init__(self):86 print("Diamond.__init__")87 super().__init__() # Calls Left.__init__8889# d = Diamond()90# Output:91# Diamond.__init__92# Left.__init__93# Right.__init__ ← super() in Left calls Right, not Base!94# Base.__init__ ← Base is called only ONCE9596# MRO: Diamond → Left → Right → Base → object9798# ============================================================99# Abstract Base Classes100# ============================================================101from abc import ABC, abstractmethod102103class Shape(ABC):104 """Abstract base class for shapes."""105106 @abstractmethod107 def area(self) -> float:108 """Calculate the area."""109 pass110111 @abstractmethod112 def perimeter(self) -> float:113 """Calculate the perimeter."""114 pass115116 def describe(self) -> str:117 """Concrete method using abstract methods."""118 return f"{self.__class__.__name__}: area={self.area():.2f}, perimeter={self.perimeter():.2f}"119120# shape = Shape() # TypeError: Can't instantiate abstract class121122class Circle(Shape):123 def __init__(self, radius: float):124 self.radius = radius125126 def area(self) -> float:127 import math128 return math.pi * self.radius ** 2129130 def perimeter(self) -> float:131 import math132 return 2 * math.pi * self.radius133134class Rectangle(Shape):135 def __init__(self, width: float, height: float):136 self.width = width137 self.height = height138139 def area(self) -> float:140 return self.width * self.height141142 def perimeter(self) -> float:143 return 2 * (self.width + self.height)144145# Polymorphism with abstract base class146shapes = [Circle(5), Rectangle(4, 6)]147for shape in shapes:148 print(shape.describe())149 # Circle: area=78.54, perimeter=31.42150 # Rectangle: area=24.00, perimeter=20.00151152# ============================================================153# Duck typing — Pythonic polymorphism154# ============================================================155class FileWriter:156 def write(self, data):157 with open("output.txt", "a") as f:158 f.write(data)159160class ConsoleWriter:161 def write(self, data):162 print(data)163164class NetworkWriter:165 def write(self, data):166 # Simulate sending data over network167 print(f"Sending: {data}")168169def save_report(writer, data):170 """Works with ANY object that has a write() method."""171 writer.write(data) # Duck typing — no type check needed172173# All three work without a shared base class174save_report(ConsoleWriter(), "Hello")175save_report(NetworkWriter(), "Hello")176177# Check capabilities instead of types178def safe_write(writer, data):179 if hasattr(writer, 'write') and callable(writer.write):180 writer.write(data)181 else:182 raise TypeError(f"{type(writer).__name__} doesn't support write()")
🏋️ Practice Exercise
Exercises:
Create a class hierarchy:
Vehicle(base) →Car,Motorcycle,Truck. Each hasfuel_efficiency()and__str__. Write a function that calculates total fuel cost for a fleet using polymorphism.Implement the diamond problem: create classes A, B(A), C(A), D(B, C) with a method
greet()in each. Trace the MRO and explain the output whenD().greet()is called withsuper().Create an ABC
Databasewith abstract methodsconnect(),query(),close(). ImplementSQLiteDB,PostgresDB, andMockDBsubclasses.Demonstrate duck typing: create three unrelated classes that all have a
serialize()method. Write a genericexport(items)function that callsserialize()on each.Implement a
Mixinpattern: createJSONSerializableMixin,LoggableMixin, andValidatableMixin. Show how a class can use multiple mixins without deep inheritance.Create a
Pluginsystem using ABCs: define aPluginBaseABC, implement several plugins, and write a loader that discovers and instantiates all plugins.
⚠️ Common Mistakes
Forgetting to call
super().__init__()in subclass constructors — parent attributes won't be initialized, causing AttributeError later.Using deep inheritance hierarchies (more than 3-4 levels). Prefer composition over inheritance:
class Car: engine = Engine()instead ofclass Car(Engine).Not understanding MRO in multiple inheritance —
super()doesn't always call the immediate parent. It follows the MRO, which can be surprising in diamond inheritance.Checking types with
type(obj) == SomeClassinstead ofisinstance(obj, SomeClass). The former doesn't work with inheritance; the latter checks the entire hierarchy.Using abstract base classes when duck typing would suffice. Python's philosophy is 'ask for forgiveness, not permission' — try the operation, handle the exception if it fails (EAFP).
💼 Interview Questions
🎤 Mock Interview
Practice a live interview for Inheritance & Polymorphism