Inheritance in Python
What is Inheritance?
Inheritance allows a class (child) to acquire the attributes and methods of another class (parent). It promotes code reuse and models real-world "is-a" relationships.
Single Inheritance
class Animal:
def __init__(self, name: str, sound: str):
self.name = name
self.sound = sound
def speak(self) -> str:
return f"{self.name} says {self.sound}"
def describe(self) -> str:
return f"I am {self.name}"
# Dog inherits from Animal
class Dog(Animal):
def __init__(self, name: str, breed: str):
super().__init__(name, "Woof") # call parent __init__
self.breed = breed
def fetch(self) -> str:
return f"{self.name} fetches the ball!"
dog = Dog("Buddy", "Labrador")
print(dog.speak()) # Buddy says Woof (inherited)
print(dog.describe()) # I am Buddy (inherited)
print(dog.fetch()) # Buddy fetches the ball! (own method)
print(dog.breed) # Labrador
# isinstance checks
print(isinstance(dog, Dog)) # True
print(isinstance(dog, Animal)) # True — Dog IS-A Animal
Method Overriding
class Shape:
def area(self) -> float:
return 0.0
def describe(self) -> str:
return f"Shape with area {self.area():.2f}"
class Circle(Shape):
def __init__(self, radius: float):
self.radius = radius
def area(self) -> float: # override parent method
import math
return math.pi * self.radius ** 2
class Rectangle(Shape):
def __init__(self, width: float, height: float):
self.width = width
self.height = height
def area(self) -> float: # override parent method
return self.width * self.height
c = Circle(5)
r = Rectangle(4, 6)
print(c.area()) # 78.54
print(r.area()) # 24.0
print(c.describe()) # Shape with area 78.54 (uses overridden area())
print(r.describe()) # Shape with area 24.00
super() — Calling Parent Methods
class Employee:
def __init__(self, name: str, salary: float):
self.name = name
self.salary = salary
def get_info(self) -> str:
return f"{self.name} — ${self.salary:,.0f}/yr"
class Manager(Employee):
def __init__(self, name: str, salary: float, team_size: int):
super().__init__(name, salary) # extend parent __init__
self.team_size = team_size
def get_info(self) -> str:
base = super().get_info() # extend parent method
return f"{base} | Team: {self.team_size}"
class Director(Manager):
def __init__(self, name: str, salary: float, team_size: int, budget: float):
super().__init__(name, salary, team_size)
self.budget = budget
def get_info(self) -> str:
base = super().get_info()
return f"{base} | Budget: ${self.budget:,.0f}"
d = Director("Alice", 150000, 20, 5000000)
print(d.get_info())
# Alice — $150,000/yr | Team: 20 | Budget: $5,000,000
Multiple Inheritance
class Flyable:
def fly(self) -> str:
return "I can fly!"
class Swimmable:
def swim(self) -> str:
return "I can swim!"
class Duck(Flyable, Swimmable):
def quack(self) -> str:
return "Quack!"
duck = Duck()
print(duck.fly()) # I can fly!
print(duck.swim()) # I can swim!
print(duck.quack()) # Quack!
# MRO — Method Resolution Order
# Python uses C3 linearization to resolve method lookup order
print(Duck.__mro__)
# (<class 'Duck'>, <class 'Flyable'>, <class 'Swimmable'>, <class 'object'>)
# Mixins — common pattern for multiple inheritance
class LogMixin:
def log(self, message: str):
print(f"[{self.__class__.__name__}] {message}")
class JsonMixin:
def to_json(self) -> str:
import json
return json.dumps(self.__dict__)
class User(LogMixin, JsonMixin):
def __init__(self, name: str, age: int):
self.name = name
self.age = age
u = User("Alice", 25)
u.log("User created") # [User] User created
print(u.to_json()) # {"name": "Alice", "age": 25}