Inheritance and Polymorphism in Python
What you'll learn
- What inheritance is and why it's useful
- How to override methods and use super()
- What polymorphism means (same message, different behavior)
- How "duck typing" works in Python
1) Inheritance: Reusing and extending classes
Think of inheritance like last names in families: a child gets features from a parent but can also have their own traits.
- Parent/base/superclass: the class you start from
- Child/sub/subclass: the class that inherits from the parent
- is-a relationship: A Dog is an Animal
Example: Animal -> Dog and Cat
class Animal:
def __init__(self, name):
self.name = name
def speak(self):
return "..." # generic/default sound
class Dog(Animal): # Dog inherits from Animal
def speak(self): # method overriding
return f"{self.name} says: Woof!"
class Cat(Animal):
def speak(self):
return f"{self.name} says: Meow!"
pets = [Dog("Rex"), Cat("Misty"), Animal("Mystery")]
for p in pets:
print(p.speak())
# Output:
# Rex says: Woof!
# Misty says: Meow!
# ...Using super() to reuse the parent's __init__:
class Person:
def __init__(self, name):
self.name = name
def info(self):
return f"Name: {self.name}"
class Student(Person):
def __init__(self, name, grade_level):
super().__init__(name) # runs Person.__init__
self.grade_level = grade_level
def info(self): # override to add more details
base = super().info() # call the parent method
return f"{base}, Grade: {self.grade_level}"
s = Student("Ari", 9)
print(s.info())
# Output: Name: Ari, Grade: 92) Polymorphism: Same call, different behavior
Polymorphism means we can write code that works with different kinds of objects as long as they follow the same "interface" (have the same method names).
Example with speak():
def make_it_speak(thing):
# Works for any object that has a .speak() method
print(thing.speak())
class Robot:
def __init__(self, robot_id):
self.robot_id = robot_id
def speak(self):
return f"Robot-{self.robot_id}: Beep boop."
crew = [Dog("Rex"), Cat("Misty"), Robot(42)]
for member in crew:
make_it_speak(member)
# Output:
# Rex says: Woof!
# Misty says: Meow!
# Robot-42: Beep boop.This is duck typing: "If it quacks like a duck, it's a duck." An object doesn't have to inherit from Animal to be used—if it has the right method, it's fine.
When to use inheritance
- You have a clear is-a relationship (Student is a Person).
- You want to share code and behavior among related classes.
Tip: If the relationship isn't is-a, consider composition (have an object inside another) instead of inheritance.
Mini Project: School Roster with Polymorphism
Goal: Build a small system that prints introductions for people at a school.
Steps
- Create a base class Person with:
- __init__(self, name)
- introduce(self) that returns "Hi, I'm {name}."
- Create two subclasses:
- Student(Person) with grade_level, override introduce to include grade
- Teacher(Person) with subject, override introduce to include subject
- Write a function welcome_all(people) that loops through a list and prints each person's introduce() message.
- Add a third class Counselor(Person) or Coach(Person) with its own twist on introduce.
- Bonus: Create a class RobotTutor with introduce(), but do NOT inherit from Person. Show that welcome_all still works (duck typing).
Starter code
class Person:
def __init__(self, name):
self.name = name
def introduce(self):
return f"Hi, I'm {self.name}."
class Student(Person):
def __init__(self, name, grade_level):
super().__init__(name)
self.grade_level = grade_level
def introduce(self):
base = super().introduce()
return f"{base} I'm in grade {self.grade_level}."
class Teacher(Person):
def __init__(self, name, subject):
super().__init__(name)
self.subject = subject
def introduce(self):
base = super().introduce()
return f"{base} I teach {self.subject}."
# 4) Add your Counselor or Coach class here
class Counselor(Person):
def introduce(self):
base = super().introduce()
return f"{base} I'm the school counselor."
def welcome_all(people):
for p in people:
print(p.introduce())
# 5) Bonus duck typing class (no inheritance)
class RobotTutor:
def __init__(self, model):
self.model = model
def introduce(self):
return f"Greetings, I am RobotTutor model {self.model}."
# Try it
people = [
Student("Ari", 9),
Teacher("Ms. Lin", "Math"),
Counselor("Mr. Patel"),
RobotTutor("RT-3000") # works due to duck typing
]
welcome_all(people)Expected output idea
- "Hi, I'm Ari. I'm in grade 9."
- "Hi, I'm Ms. Lin. I teach Math."
- "Hi, I'm Mr. Patel. I'm the school counselor."
- "Greetings, I am RobotTutor model RT-3000."
Summary
- Inheritance lets one class reuse and extend another class's code (is-a relationship).
- Overriding lets a subclass change how a method works.
- super() calls the parent's version so you can add to it, not just replace it.
- Polymorphism means the same method name can work across different classes.
- Duck typing: if an object has the needed methods, it can be used—no inheritance required.