← View series: python tutorials
~/blog
OOP - Inheritance & Polymorphism
Introduction
In this tutorial, you'll learn about two fundamental OOP concepts: inheritance and polymorphism. Inheritance allows you to create new classes based on existing ones, while polymorphism allows objects of different classes to be treated uniformly.
What You'll Learn
- What is inheritance
- Creating subclasses
- The super() function
- Method overriding
- Multiple inheritance
- What is polymorphism
- Duck typing
What is Inheritance?
Inheritance is a way to create a new class from an existing class. The new class (child/subclass) inherits attributes and methods from the existing class (parent/superclass).
Why Use Inheritance?
| Benefit | Description |
|---|---|
| Code Reuse | Don't write same code twice |
| Organization | Clear relationship between classes |
| Extensibility | Add features without modifying parent |
| Maintainability | Changes in one place affect all children |
Basic Inheritance
# Parent class (also called base class)
class Animal:
def __init__(self, name):
self.name = name
def speak(self):
return "Some sound"
def eat(self):
return f"{self.name} is eating"
# Child class (inherits from Animal)
class Dog(Animal):
def speak(self):
return "Woof!"
# Create instances
animal = Animal("Generic Animal")
dog = Dog("Buddy")
print(animal.speak()) # Some sound
print(dog.speak()) # Woof!
print(dog.eat()) # Buddy is eatingThe super() Function
The super() function allows you to call methods from the parent class:
class Animal:
def __init__(self, name):
self.name = name
def __str__(self):
return f"Animal: {self.name}"
class Dog(Animal):
def __init__(self, name, breed):
# Call parent's __init__
super().__init__(name)
self.breed = breed
def __str__(self):
return f"Dog: {self.name} ({self.breed})"
def speak(self):
return "Woof!"
dog = Dog("Buddy", "Golden Retriever")
print(dog) # Dog: Buddy (Golden Retriever)
print(dog.speak()) # Woof!Method Overriding
Child classes can override (replace) methods from the parent class:
class Shape:
def area(self):
return 0
def perimeter(self):
return 0
class Rectangle(Shape):
def __init__(self, width, height):
self.width = width
self.height = height
# Override area method
def area(self):
return self.width * self.height
# Override perimeter method
def perimeter(self):
return 2 * (self.width + self.height)
class Circle(Shape):
def __init__(self, radius):
self.radius = radius
def area(self):
return 3.14159 * self.radius ** 2
def perimeter(self):
return 2 * 3.14159 * self.radius
# Test
rect = Rectangle(5, 3)
circle = Circle(2)
print(f"Rectangle area: {rect.area()}") # 15
print(f"Rectangle perimeter: {rect.perimeter()}") # 16
print(f"Circle area: {circle.area()}") # 12.56636
print(f"Circle perimeter: {circle.perimeter()}") # 12.56636Multiple Inheritance
A class can inherit from multiple parent classes:
class Flyable:
def fly(self):
return "Flying!"
class Swimmable:
def swim(self):
return "Swimming!"
# Duck inherits from both
class Duck(Flyable, Swimmable):
def speak(self):
return "Quack!"
duck = Duck()
print(duck.fly()) # Flying!
print(duck.swim()) # Swimming!
print(duck.speak()) # Quack!⚠️ Warning: Multiple inheritance can lead to complexity. Use it carefully.
Method Resolution Order (MRO)
Python uses MRO to determine which method to call:
class A:
def greet(self):
return "Hello from A"
class B(A):
def greet(self):
return "Hello from B"
class C(A):
def greet(self):
return "Hello from C"
class D(B, C):
pass
d = D()
print(d.greet()) # Hello from B (B comes before C in MRO)
print(D.__mro__) # Show the orderPractical Example: Employee System
class Employee:
def __init__(self, name, employee_id, salary):
self.name = name
self.employee_id = employee_id
self.salary = salary
def get_details(self):
return f"{self.name} (ID: {self.employee_id})"
def work(self):
return f"{self.name} is working"
class Manager(Employee):
def __init__(self, name, employee_id, salary, department):
super().__init__(name, employee_id, salary)
self.department = department
self.team = []
def add_team_member(self, employee):
self.team.append(employee)
def get_team_size(self):
return len(self.team)
def work(self):
return f"{self.name} is managing the {self.department} department"
class Developer(Employee):
def __init__(self, name, employee_id, salary, programming_language):
super().__init__(name, employee_id, salary)
self.programming_language = programming_language
def work(self):
return f"{self.name} is coding in {self.programming_language}"
# Create employees
manager = Manager("Alice", "M001", 80000, "Engineering")
dev1 = Developer("Bob", "D001", 60000, "Python")
dev2 = Developer("Charlie", "D002", 65000, "JavaScript")
# Add team members
manager.add_team_member(dev1)
manager.add_team_member(dev2)
# Show details
print(manager.get_details())
print(f"Team size: {manager.get_team_size()}")
print(dev1.get_details())
print(f"Language: {dev1.programming_language}")
# Polymorphism in action
employees = [manager, dev1, dev2]
print("\n--- All employees working ---")
for emp in employees:
print(emp.work())Output:
Alice (ID: M001)
Team size: 2
Bob (ID: D001)
Language: Python
--- All employees working ---
Alice is managing the Engineering department
Bob is coding in Python
Charlie is coding in JavaScript
What is Polymorphism?
Polymorphism means "many forms". It allows the same interface (method) to work with different types of objects.
Duck Typing
"If it walks like a duck and quacks like a duck, it's a duck":
class Duck:
def speak(self):
return "Quack!"
class Cat:
def speak(self):
return "Meow!"
class Dog:
def speak(self):
return "Woof!"
# Any object with speak() method works
def make_speak(animal):
return animal.speak()
duck = Duck()
cat = Cat()
dog = Dog()
print(make_speak(duck)) # Quack!
print(make_speak(cat)) # Meow!
print(make_speak(dog)) # Woof!Polymorphism with Inheritance
class Vehicle:
def move(self):
pass
class Car(Vehicle):
def move(self):
return "Driving 🚗"
class Plane(Vehicle):
def move(self):
return "Flying ✈️"
class Boat(Vehicle):
def move(self):
return "Sailing 🚤"
# Polymorphic function
def start_journey(vehicle):
print(f"Starting: {vehicle.move()}")
# Different vehicles, same interface
car = Car()
plane = Plane()
boat = Boat()
start_journey(car) # Starting: Driving 🚗
start_journey(plane) # Starting: Flying ✈️
start_journey(boat) # Starting: Sailing 🚤isinstance() and issubclass()
Check object and class relationships:
class Animal:
pass
class Dog(Animal):
pass
class Cat(Animal):
pass
# Check instance
dog = Dog()
print(isinstance(dog, Dog)) # True
print(isinstance(dog, Animal)) # True (Dog is subclass of Animal)
print(isinstance(dog, Cat)) # False
# Check subclass
print(issubclass(Dog, Animal)) # True
print(issubclass(Cat, Animal)) # True
print(issubclass(Animal, Dog)) # FalseSummary
In this tutorial, you learned:
- ✅ What is inheritance
- ✅ Creating subclasses
- ✅ The super() function
- ✅ Method overriding
- ✅ Multiple inheritance
- ✅ What is polymorphism
- ✅ Duck typing
🧑💻 Practice Exercise
Create an inheritance hierarchy for a school system:
Personclass (parent) with name, ageStudentclass that inherits from Person, adds gradeTeacherclass that inherits from Person, adds subjectTeachingAssistantthat inherits from both Student and Teacher- Use super() to properly initialize all attributes
Click to see solution
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def get_info(self):
return f"{self.name}, {self.age} years old"
class Student(Person):
def __init__(self, name, age, grade):
super().__init__(name, age)
self.grade = grade
def study(self):
return f"{self.name} is studying for grade {self.grade}"
def get_info(self):
return f"{self.name}, {self.age} years old, Grade: {self.grade}"
class Teacher(Person):
def __init__(self, name, age, subject):
super().__init__(name, age)
self.subject = subject
def teach(self):
return f"{self.name} is teaching {self.subject}"
def get_info(self):
return f"{self.name}, {self.age} years old, Teaches: {self.subject}"
class TeachingAssistant(Student, Teacher):
def __init__(self, name, age, grade, subject):
Student.__init__(self, name, age, grade)
Teacher.__init__(self, name, age, subject)
def get_info(self):
return f"{self.name}, {self.age} years old, Grade: {self.grade}, TA for: {self.subject}"
def assist(self):
return f"{self.name} is assisting with {self.subject}"
# Create instances
student = Student("Alice", 16, "10th")
teacher = Teacher("Mr. Smith", 35, "Mathematics")
ta = TeachingAssistant("Bob", 22, "Graduate", "Computer Science")
# Test
print("=== Student ===")
print(student.get_info())
print(student.study())
print("\n=== Teacher ===")
print(teacher.get_info())
print(teacher.teach())
print("\n=== Teaching Assistant ===")
print(ta.get_info())
print(ta.study())
print(ta.teach())
print(ta.assist())
# Show MRO
print(f"\nTeachingAssistant MRO: {TeachingAssistant.__mro__}")Output:
=== Student ===
Alice, 16 years old, Grade: 10th
Alice is studying for grade 10th
=== Teacher ===
Mr. Smith, 35 years old, Teaches: Mathematics
Mr. Smith is teaching Mathematics
=== Teaching Assistant ===
Bob, 22 years old, Grade: Graduate, TA for: Computer Science
Bob is studying for grade Graduate
Bob is teaching Computer Science
Bob is assisting with Computer Science
TeachingAssistant MRO: (<class '__main__.TeachingAssistant'>, <class '__main__.Student'>, <class '__main__.Teacher'>, <class '__main__.Person'>, <class 'object'>)
What's Next
In the next tutorial, we'll learn about OOP - Advanced Concepts - encapsulation, dunder methods, and more.