Polymorphism is one of the fundamental concepts of Object-Oriented Programming (OOP) that allows different classes to define methods with the same name but different behaviors.
Why Use Polymorphism?
Code Flexibility – Write generic code that works with multiple object types
Code Reusability – Use the same method name for different operations
Extensibility – Easily extend functionality without modifying existing code
1. What is Polymorphism?
Polymorphism means “many forms.” It allows the same method or function to behave differently based on the object calling it.
Example of Polymorphism
class Dog:
def sound(self):
return "Bark"
class Cat:
def sound(self):
return "Meow"
# Function demonstrating polymorphism
def animal_sound(animal):
print(animal.sound())
dog = Dog()
cat = Cat()
animal_sound(dog) # Output: Bark
animal_sound(cat) # Output: Meow
How does this work?
Even though Dog
and Cat
have different implementations of sound()
, the same function animal_sound()
works for both.
2. Types of Polymorphism in Python
Python supports three types of polymorphism:
- Method Overriding (Runtime Polymorphism)
- Method Overloading (Compile-Time Polymorphism, simulated in Python)
- Operator Overloading
3. Method Overriding (Runtime Polymorphism)
Method overriding allows a child class to redefine a method from the parent class.
Example: Method Overriding
class Animal:
def sound(self):
return "Some sound"
class Dog(Animal):
def sound(self): # Overriding parent method
return "Bark"
class Cat(Animal):
def sound(self): # Overriding parent method
return "Meow"
# Using overridden methods
dog = Dog()
cat = Cat()
print(dog.sound()) # Output: Bark
print(cat.sound()) # Output: Meow
Key Points:
- The method
sound()
is defined in the parent class but overridden in the child classes. - The method in the child class replaces the parent class method when called from an instance of the child class.
4. Method Overloading (Simulated in Python)
Python does not support method overloading (same method name with different parameters) like Java or C++, but it can be simulated using default arguments or *args
.
Example: Simulating Method Overloading
class MathOperations:
def add(self, a, b, c=0): # Default value allows different numbers of arguments
return a + b + c
math_obj = MathOperations()
print(math_obj.add(2, 3)) # Output: 5
print(math_obj.add(2, 3, 4)) # Output: 9
Key Points:
- Python allows default arguments to mimic method overloading.
- We use
c=0
as a default argument to handle different cases. - Another way to achieve this is with
*args
(variable-length arguments).
Example: Using *args
for Method Overloading
class Calculator:
def add(self, *args): # Accepts any number of arguments
return sum(args)
calc = Calculator()
print(calc.add(2, 3)) # Output: 5
print(calc.add(2, 3, 4, 5)) # Output: 14
Advantage: Works for any number of arguments.
5. Operator Overloading in Python
Operator overloading allows operators (+
, -
, *
, etc.) to be redefined for custom objects.
Example: Operator Overloading with +
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __add__(self, other): # Overloading "+"
return Point(self.x + other.x, self.y + other.y)
def __str__(self):
return f"({self.x}, {self.y})"
p1 = Point(2, 3)
p2 = Point(4, 5)
result = p1 + p2 # Calls __add__()
print(result) # Output: (6, 8)
Key Points:
__add__()
defines how+
works forPoint
objects.p1 + p2
callsp1.__add__(p2)
.- Similar methods exist for
-
(__sub__
),*
(__mul__
), and/
(__truediv__
).
6. Polymorphism with Abstract Classes (Using abc
Module)
Abstract classes enforce that subclasses must implement certain methods.
from abc import ABC, abstractmethod
class Animal(ABC): # Abstract class
@abstractmethod
def sound(self):
pass # Must be implemented in child classes
class Dog(Animal):
def sound(self):
return "Bark"
class Cat(Animal):
def sound(self):
return "Meow"
dog = Dog()
cat = Cat()
print(dog.sound()) # Output: Bark
print(cat.sound()) # Output: Meow
Key Points:
ABC
(Abstract Base Class) ensuressound()
must be implemented in subclasses.- If a subclass does not implement
sound()
, it raises an error.
7. Real-World Example: Payment System
class Payment:
def process_payment(self, amount):
pass # Placeholder for child classes
class CreditCardPayment(Payment):
def process_payment(self, amount):
print(f"Processing credit card payment of ${amount}")
class PayPalPayment(Payment):
def process_payment(self, amount):
print(f"Processing PayPal payment of ${amount}")
# Function using polymorphism
def make_payment(payment_method, amount):
payment_method.process_payment(amount)
cc = CreditCardPayment()
paypal = PayPalPayment()
make_payment(cc, 100) # Output: Processing credit card payment of $100
make_payment(paypal, 50) # Output: Processing PayPal payment of $50
Key Points:
make_payment()
works with any Payment subclass, enabling polymorphism.- Easily extendable by adding new payment methods without modifying existing code.