Inheritance
Inheritance allows you to create a new class based on an existing class. The new class (child) inherits all the attributes and methods from the parent class, and you can add new features or modify existing ones.
Think of it like a family tree - a child inherits traits from parents but can also have their own unique features.
To demonstrate inheritance, consider the following example where we want to represent different types of vehicles:
❌ Without Inheritance
# vehicles.py class Car: def __init__(self, brand, model): self.brand = brand self.model = model self.wheels = 4 def start(self): print(f"{self.brand} {self.model} car is starting...") def drive(self): print(f"{self.brand} {self.model} car is driving...") class Motorcycle: def __init__(self, brand, model): self.brand = brand self.model = model self.wheels = 2 def start(self): print(f"{self.brand} {self.model} motorcycle is starting...") def drive(self): print(f"{self.brand} {self.model} motorcycle is driving...") class Truck: def __init__(self, brand, model): self.brand = brand self.model = model self.wheels = 6 def start(self): print(f"{self.brand} {self.model} truck is starting...") def drive(self): print(f"{self.brand} {self.model} truck is driving...")# vehicles.py class Car: def __init__(self, brand, model): self.brand = brand self.model = model self.wheels = 4 def start(self): print(f"{self.brand} {self.model} car is starting...") def drive(self): print(f"{self.brand} {self.model} car is driving...") class Motorcycle: def __init__(self, brand, model): self.brand = brand self.model = model self.wheels = 2 def start(self): print(f"{self.brand} {self.model} motorcycle is starting...") def drive(self): print(f"{self.brand} {self.model} motorcycle is driving...") class Truck: def __init__(self, brand, model): self.brand = brand self.model = model self.wheels = 6 def start(self): print(f"{self.brand} {self.model} truck is starting...") def drive(self): print(f"{self.brand} {self.model} truck is driving...")
Problem: We're repeating the same code (brand, model, start, drive) in every class. If we want to change how vehicles work, we have to update multiple places.
✅ With Inheritance
# vehicle.py class Vehicle: def __init__(self, brand, model, wheels): self.brand = brand self.model = model self.wheels = wheels def start(self): print(f"{self.brand} {self.model} is starting...") def drive(self): print(f"{self.brand} {self.model} is driving...")# vehicle.py class Vehicle: def __init__(self, brand, model, wheels): self.brand = brand self.model = model self.wheels = wheels def start(self): print(f"{self.brand} {self.model} is starting...") def drive(self): print(f"{self.brand} {self.model} is driving...")
# car.py from vehicle import Vehicle class Car(Vehicle): def __init__(self, brand, model): super().__init__(brand, model, wheels=4) # Call parent's __init__ def honk(self): print(f"{self.brand} {self.model} is honking!")# car.py from vehicle import Vehicle class Car(Vehicle): def __init__(self, brand, model): super().__init__(brand, model, wheels=4) # Call parent's __init__ def honk(self): print(f"{self.brand} {self.model} is honking!")
# motorcycle.py from vehicle import Vehicle class Motorcycle(Vehicle): def __init__(self, brand, model): super().__init__(brand, model, wheels=2) def wheelie(self): print(f"{self.brand} {self.model} is doing a wheelie!")# motorcycle.py from vehicle import Vehicle class Motorcycle(Vehicle): def __init__(self, brand, model): super().__init__(brand, model, wheels=2) def wheelie(self): print(f"{self.brand} {self.model} is doing a wheelie!")
# truck.py from vehicle import Vehicle class Truck(Vehicle): def __init__(self, brand, model): super().__init__(brand, model, wheels=6) def load_cargo(self): print(f"{self.brand} {self.model} is loading cargo...")# truck.py from vehicle import Vehicle class Truck(Vehicle): def __init__(self, brand, model): super().__init__(brand, model, wheels=6) def load_cargo(self): print(f"{self.brand} {self.model} is loading cargo...")
# main.py from car import Car from motorcycle import Motorcycle from truck import Truck car = Car("Toyota", "Camry") car.start() car.drive() car.honk() motorcycle = Motorcycle("Honda", "CBR") motorcycle.start() motorcycle.drive() motorcycle.wheelie() truck = Truck("Ford", "F-150") truck.start() truck.drive() truck.load_cargo()# main.py from car import Car from motorcycle import Motorcycle from truck import Truck car = Car("Toyota", "Camry") car.start() car.drive() car.honk() motorcycle = Motorcycle("Honda", "CBR") motorcycle.start() motorcycle.drive() motorcycle.wheelie() truck = Truck("Ford", "F-150") truck.start() truck.drive() truck.load_cargo()
Now:
- All vehicles inherit common features from
Vehicle. - Each vehicle type can have its own unique methods.
- Changes to
Vehicleautomatically apply to all child classes.
This approach makes the code:
- ✅ DRY (Don't Repeat Yourself) - no code duplication
- ✅ Easier to maintain - update once, affects all
- ✅ More organized - clear hierarchy
🎁 Bonus: Types of Inheritance, super(), and MRO in Python
Types of Inheritance in Python
Python supports several inheritance types:
Single Inheritance
One parent → one child
class A: pass class B(A): passclass A: pass class B(A): pass
Multilevel Inheritance
Grandparent → Parent → Child
class A: pass class B(A): pass class C(B): passclass A: pass class B(A): pass class C(B): pass
Hierarchical Inheritance
One parent → multiple children
class A: pass class B(A): pass class C(A): passclass A: pass class B(A): pass class C(A): pass
Multiple Inheritance
Child inherits from multiple parents
class A: pass class B: pass class C(A, B): passclass A: pass class B: pass class C(A, B): pass
The super() Keyword (Simple Explanation)
super() lets a child class call methods from its parent class.
class Vehicle: def __init__(self, brand): self.brand = brand class Car(Vehicle): def __init__(self, brand, model): super().__init__(brand) self.model = modelclass Vehicle: def __init__(self, brand): self.brand = brand class Car(Vehicle): def __init__(self, brand, model): super().__init__(brand) self.model = model
Why use super()?
- Avoid repeating parent logic
- Cleaner inheritance
- Works correctly with multiple inheritance through MRO
MRO (Method Resolution Order)
MRO defines the order Python follows to search for methods.
Example:
class A: pass class B: pass class C(A, B): pass print(C.mro())class A: pass class B: pass class C(A, B): pass print(C.mro())
Purpose of MRO:
- Decides which class is checked first
- Prevents confusion in multiple inheritance
- Follows C3 Linearization (Python handles it automatically)
Quick Summary
- Types: Single, Multilevel, Hierarchical, Multiple
super()calls parent methods cleanly- MRO decides method lookup order, especially in multiple inheritance