Polymorphism is one of the fundamental principles of Object-Oriented Programming (OOP). The term comes from the Greek words poly (many) and morph (forms), meaning "many forms". In Python, polymorphism allows objects of different classes to be treated as objects of a common superclass, especially when they share the same method names or behaviour.

Why-Polymorphism ?

Polymorphism enables:

  • Code Reusability: Write general code applicable to many object types.
  • Flexibility: Add new object types without modifying existing code.
  • Improved Readability: Abstract high-level behavior without delving into specifics.

How Polymorphism Works in Python

Python supports polymorphism through:

  • Duck Typing: "If it walks like a duck and quacks like a duck, it must be a duck."
  • Inheritance and Method Overriding: Child classes override or implement methods of a parent class.
  • Operator Overloading: Define custom behavior for operators like +, *, etc., based on object type.

Real-Life Analogy

Consider a Media Player:
• It can play different media types like audio (.mp3) and video (.mp4).
• You don’t need separate players for audio and video; a single player supports both.
• This is polymorphism: the media player behaves differently depending on the media type.

Examples on Polymorphism in Python


1. Polymorphism with Functions

A single function can work with objects of different types if those objects implement the expected methods.

Example 1:

    class Dog:
    def sound(self):
        return "Woof!"
class Cat:
    def sound(self):
        return "Meow!"
def animal_sound(animal):
    print(animal.sound())
# Both Dog and Cat are treated the same way
dog = Dog()
cat = Cat()
animal_sound(dog)  # Output: Woof!
animal_sound(cat)  # Output: Meow!

2. Polymorphism with Inheritance

Inheritance allows child classes to override parent class methods, enabling polymorphic behavior.

Example 2:

class Shape:
    def area(self):
        raise NotImplementedError
("This method should be overridden by subclasses")

class Circle(Shape):
    def __init__(self, radius):
        self.radius = radius

    def area(self):
        return 3.14 * self.radius ** 2

class Rectangle(Shape):
    def __init__(self, width, height):
        self.width = width
        self.height = height

    def area(self):
        return self.width * self.height

shapes = [Circle(5), Rectangle(4, 6)]

for shape in shapes:
    print(f"Area: {shape.area()}")  # Calls the overridden area method

3. Polymorphism with Abstract Classes

Abstract classes define a common interface for their subclasses. Subclasses must implement the abstract methods.

Example 3:

from abc import ABC, abstractmethod
class Vehicle(ABC):
    @abstractmethod
    def move(self):
        pass
class Car(Vehicle):
    def move(self):
        return "Driving on the road"
class Boat(Vehicle):
    def move(self):
        return "Sailing on water"
vehicles = [Car(), Boat()]
for vehicle in vehicles:
    print(vehicle.move())

4. Operator Overloading

Operators like + and * can behave differently for custom objects by implementing special methods like __add__ and __mul__.

Example 4:

class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y
    def __add__(self, other):
        return Point(self.x + other.x, self.y + other.y)
    def __repr__(self):
        return f"Point({self.x}, {self.y})"
p1 = Point(1, 2)
p2 = Point(3, 4)
print(p1 + p2)  # Output: Point(4, 6)

Example 5: File Reader

A function to read content can work with different file types like .txt, .csv, and .json.

class TextFile:
    def read(self):
        return "Reading text file"

class CSVFile:
    def read(self):
        return "Reading CSV file"

class JSONFile:
    def read(self):
        return "Reading JSON file"

def read_file(file):
    print(file.read())

files = [TextFile(), CSVFile(), JSONFile()]

for file in files:
    read_file(file)

Duck Typing in Python

Python is dynamically typed, so it doesn’t check the type of an object explicitly. It just checks whether the object has the required behavior.

Example 6:

class Pen:
    def write(self):
        return "Writing with a pen"
class Keyboard:
    def write(self):
        return "Typing on a keyboard"
def write_something(tool):
    print(tool.write())
pen = Pen()
keyboard = Keyboard()
write_something(pen)       # Writing with a pen
write_something(keyboard)  # Typing on a keyboard

Best Practices

  • Use Polymorphism for Abstraction: Write generic code and rely on polymorphism for specific implementations.
  • Avoid Explicit Type Checking: Let objects determine their behavior through method implementation.
  • Use Abstract Classes for Contracts: Define a clear interface for subclasses when building polymorphic systems.

When to use Polymorphism?

When to use Polymorphism?

  • When you need to work with objects of different types in a unified way.
  • When designing libraries or frameworks with extensibility in mind.
  • When applying design patterns like Strategy, Factory, or Observer.

Summary

• Polymorphism allows objects of different types to share a common interface and behavior.

• Python achieves polymorphism via:

o Duck typing.
o Method overriding.
o Abstract base classes.
o Operator overloading.

• Enables flexible, reusable, and scalable code.