What is Polymorphism?
Polymorphism means "many forms." In Python, polymorphism allows the same interface, method, or operation to work with different types of objects.
The same method name can perform different actions depending on the object that calls it. This makes programs flexible, reusable, and easier to extend.
Method Overriding
Different child classes can provide their own implementation of the same method.
Duck Typing
Python focuses on what an object can do rather than its exact class type.
Operator Overloading
Operators such as +, *, and == can behave differently for different objects.
Function Polymorphism
The same function can work with different types of objects or data.
Different classes can have a method with the same name, but each class can implement that method differently.
class Dog:
def sound(self):
print("Dog says: Woof!")
class Cat:
def sound(self):
print("Cat says: Meow!")
dog = Dog()
cat = Cat()
dog.sound()
cat.sound()
Dog says: Woof!
Cat says: Meow!
Both objects use the same sound() method name,
but the behavior is different.
Polymorphism is commonly used with inheritance. A parent class defines a common method, and child classes override it with their own behavior.
class Employee:
def work(self):
print("Employee is working")
class Developer(Employee):
def work(self):
print("Developer is writing code")
class Designer(Employee):
def work(self):
print("Designer is creating designs")
developer = Developer()
designer = Designer()
developer.work()
designer.work()
Developer is writing code
Designer is creating designs
The same work() method behaves differently
for each child object.
In a payment application, different payment methods can
provide their own implementation of a common
pay() method.
class CreditCard:
def pay(self, amount):
print("Paid", amount, "using Credit Card")
class UPI:
def pay(self, amount):
print("Paid", amount, "using UPI")
class NetBanking:
def pay(self, amount):
print("Paid", amount, "using Net Banking")
payments = [
CreditCard(),
UPI(),
NetBanking()
]
for payment in payments:
payment.pay(2500)
Paid 2500 using Credit Card
Paid 2500 using UPI
Paid 2500 using Net Banking
The loop does not need to know the exact class of each
payment object. It simply calls pay().
A notification system may send messages through email,
SMS, or push notifications. Each class can implement
send() differently.
class EmailNotification:
def send(self, message):
print("Email:", message)
class SMSNotification:
def send(self, message):
print("SMS:", message)
class PushNotification:
def send(self, message):
print("Push Notification:", message)
notifications = [
EmailNotification(),
SMSNotification(),
PushNotification()
]
for notification in notifications:
notification.send("Your order has been shipped.")
Email: Your order has been shipped.
SMS: Your order has been shipped.
Push Notification: Your order has been shipped.
Python follows the idea of duck typing: "If it behaves like the required object, it can be used."
class Duck:
def fly(self):
print("Duck is flying")
class Bird:
def fly(self):
print("Bird is flying")
class Airplane:
def fly(self):
print("Airplane is flying")
def make_it_fly(obj):
obj.fly()
make_it_fly(Duck())
make_it_fly(Bird())
make_it_fly(Airplane())
Duck is flying
Bird is flying
Airplane is flying
The function does not check the object's class. It only
expects the object to provide a fly() method.
A single function can work with different objects as long as those objects support the required operation.
def calculate_total(a, b):
return a + b
print(calculate_total(10, 20))
print(calculate_total(10.5, 20.5))
print(calculate_total("Hello ", "Python"))
30
31.0
Hello Python
The + operation works differently depending
on the data type: numbers are added while strings are
concatenated.
Python operators can perform different operations depending on the types of objects involved.
print(10 + 20)
print("Python " + "Programming")
print([1, 2] + [3, 4])
print(5 * 3)
print("Hi " * 3)
30
Python Programming
[1, 2, 3, 4]
15
Hi Hi Hi
The same operators can have different meanings for different Python data types.
A child class can override a parent method while still
calling the original parent implementation using
super().
class Employee:
def work(self):
print("Employee is working")
class Developer(Employee):
def work(self):
super().work()
print("Developer is writing Python code")
developer = Developer()
developer.work()
Employee is working
Developer is writing Python code
Different vehicles can implement the same
start() method according to their own
behavior.
class Car:
def start(self):
print("Car engine started")
class Bike:
def start(self):
print("Bike engine started")
class ElectricCar:
def start(self):
print("Electric motor started")
vehicles = [
Car(),
Bike(),
ElectricCar()
]
for vehicle in vehicles:
vehicle.start()
Car engine started
Bike engine started
Electric motor started
Different types of employees may calculate their salary
differently while exposing the same
calculate_salary() method.
class FullTimeEmployee:
def calculate_salary(self):
return 60000
class PartTimeEmployee:
def calculate_salary(self):
return 30000
class ContractEmployee:
def calculate_salary(self):
return 45000
employees = [
FullTimeEmployee(),
PartTimeEmployee(),
ContractEmployee()
]
for employee in employees:
print("Salary:", employee.calculate_salary())
Salary: 60000
Salary: 30000
Salary: 45000
Different product types can calculate discounts using the same method name.
class Electronics:
def discount(self, price):
return price * 0.10
class Clothing:
def discount(self, price):
return price * 0.20
class Grocery:
def discount(self, price):
return price * 0.05
products = [
Electronics(),
Clothing(),
Grocery()
]
price = 10000
for product in products:
print("Discount:", product.discount(price))
Discount: 1000.0
Discount: 2000.0
Discount: 500.0
Different shapes can implement the same
area() method while using different formulas.
class Rectangle:
def __init__(self, length, width):
self.length = length
self.width = width
def area(self):
return self.length * self.width
class Circle:
def __init__(self, radius):
self.radius = radius
def area(self):
return 3.14 * self.radius * self.radius
class Square:
def __init__(self, side):
self.side = side
def area(self):
return self.side * self.side
shapes = [
Rectangle(10, 5),
Circle(7),
Square(6)
]
for shape in shapes:
print("Area:", shape.area())
Area: 50
Area: 153.86
Area: 36
The loop simply calls area(). Each object
calculates the result using its own implementation.
Different food items can implement a common
prepare() method.
class Pizza:
def prepare(self):
print("Preparing Pizza")
class Burger:
def prepare(self):
print("Preparing Burger")
class Biryani:
def prepare(self):
print("Preparing Biryani")
orders = [
Pizza(),
Burger(),
Biryani()
]
for order in orders:
order.prepare()
Preparing Pizza
Preparing Burger
Preparing Biryani
Doctors, nurses, and other healthcare professionals may perform different actions through a common interface.
class Doctor:
def perform_duty(self):
print("Doctor is diagnosing patients")
class Nurse:
def perform_duty(self):
print("Nurse is caring for patients")
class Pharmacist:
def perform_duty(self):
print("Pharmacist is preparing medicines")
staff = [
Doctor(),
Nurse(),
Pharmacist()
]
for person in staff:
person.perform_duty()
Doctor is diagnosing patients
Nurse is caring for patients
Pharmacist is preparing medicines
Python allows classes to define how operators behave for their objects. This is called operator overloading.
class Product:
def __init__(self, price):
self.price = price
def __add__(self, other):
return self.price + other.price
product1 = Product(1000)
product2 = Product(1500)
total = product1 + product2
print("Total Price:", total)
Total Price: 2500
The + operator is customized using the
special method __add__().
This example combines inheritance and polymorphism in a simple notification system.
class Notification:
def send(self, message):
print("Sending notification:", message)
class EmailNotification(Notification):
def send(self, message):
print("Sending Email:", message)
class SMSNotification(Notification):
def send(self, message):
print("Sending SMS:", message)
class PushNotification(Notification):
def send(self, message):
print("Sending Push Notification:", message)
notifications = [
EmailNotification(),
SMSNotification(),
PushNotification()
]
message = "Your order has been delivered."
for notification in notifications:
notification.send(message)
Sending Email: Your order has been delivered.
Sending SMS: Your order has been delivered.
Sending Push Notification: Your order has been delivered.
The loop uses the same
notification.send() statement for all three
objects, but each object produces different behavior.
This is polymorphism in practice.
| Concept | Purpose | Example |
|---|---|---|
| Inheritance | Reuse properties and methods from another class. | Developer inherits Employee |
| Polymorphism | Same interface produces different behavior. | Developer.work() and Designer.work() |
| Method Overriding | Child class provides its own implementation of a parent method. | Developer overrides work() |
| Duck Typing | Object capability matters more than its exact class. | Any object with fly() |
| Operator Overloading | Customize operators for user-defined objects. | __add__() |
Rule 1: Same interface, different behavior
Different objects can respond differently to the same method call.
Rule 2: Method overriding enables runtime polymorphism
Child classes can override methods inherited from their parent classes.
Rule 3: Python supports duck typing
Python generally cares about whether an object supports the required operation rather than requiring a specific class.
Rule 4: Functions can work with multiple object types
A function can accept different objects when they provide the methods or operations the function needs.
Rule 5: Operators can have multiple meanings
Operators such as + can perform different
operations depending on the objects involved.