Polymorphism

3 min read

Making Objects Behave Differently with the Same Interface

The word Polymorphism comes from Greek, meaning “many forms.”

In programming, polymorphism means the ability of different classes to be treated through a common interface but behave differently depending on their actual class.

In simple terms:
Different objects respond to the same method call in different ways.

Why Polymorphism Matters? #

Imagine you have different types of animals, and you want to tell them to make a sound. You don’t want to write different code for each animal every time. You just want to call:

animal.make_sound()

And have each animal respond correctly based on what kind it is — dog barks, cat meows, bird chirps.

Polymorphism makes this possible by:

  • Simplifying code: You write code once and it works with many types.
  • Enhancing flexibility: Your program can handle new types easily without changing the existing code.
  • Supporting code extensibility and maintainability.

How Does Polymorphism Work in Python? #

Python achieves polymorphism mainly through:

  1. Method Overriding (Inheritance-based polymorphism)
  2. Duck Typing (Python’s dynamic typing philosophy)

1. Method Overriding: Polymorphism with Inheritance #

When a child class provides its own version of a method already defined in the parent class, it is called method overriding. This enables polymorphism because you can call the method on any object, and the correct version will be executed based on the object’s class.

Example: Animals and Sounds #

class Animal:
    def speak(self):
        print("Animal makes a sound")

class Dog(Animal):
    def speak(self):
        print("Dog barks")

class Cat(Animal):
    def speak(self):
        print("Cat meows")

def animal_sound(animal):
    animal.speak()

dog = Dog()
cat = Cat()
generic_animal = Animal()

animal_sound(dog)            # Dog barks
animal_sound(cat)            # Cat meows
animal_sound(generic_animal) # Animal makes a sound

What’s happening here? #

  • The function animal_sound accepts any object.
  • It calls .speak() on the passed object.
  • Python uses the actual class of the object to decide which speak method to execute.
  • This is polymorphism in action — one interface (speak) with multiple forms (dog, cat, generic animal).

2. Duck Typing: If It Walks Like a Duck… #

Python is dynamically typed and uses a concept called duck typing — if an object behaves like a duck (has the required methods or attributes), Python treats it like a duck.

You don’t have to inherit from a common base class. Any object with the needed method can be used.


Example: Different classes, no inheritance needed #

class Bird:
    def fly(self):
        print("Bird is flying")

class Airplane:
    def fly(self):
        print("Airplane is flying")

def let_it_fly(flying_object):
    flying_object.fly()

bird = Bird()
plane = Airplane()

let_it_fly(bird)   # Bird is flying
let_it_fly(plane)  # Airplane is flying

Why this is cool? #

  • let_it_fly doesn’t care what type flying_object is.
  • As long as flying_object has a .fly() method, it works.
  • You get polymorphism without inheritance.

Polymorphism with Built-in Functions #

Python’s polymorphism is used even in built-in functions like len(). Different types respond to len() in their own way:

print(len("Hello"))       # 5, length of string
print(len([1, 2, 3]))    # 3, length of list
print(len({"a": 1, "b": 2}))  # 2, number of keys in dict

len() calls the underlying __len__ method for each type.


Polymorphism with Operator Overloading #

Python lets classes define how operators behave on their instances via special methods, which is a form of polymorphism.

Example:

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 __str__(self):
        return f"({self.x}, {self.y})"

p1 = Point(1, 2)
p2 = Point(3, 4)

print(p1 + p2)  # (4, 6)

Here, + behaves differently for Point objects than for integers — a neat example of polymorphism.


Real-World Use Case: Shape Drawing System #

Suppose you are building a graphics system that draws different shapes: circles, rectangles, triangles. Each shape can calculate its area, but the calculation differs.

Step 1: Base Shape class #

class Shape:
    def area(self):
        pass  # Base method, no implementation

Step 2: Child classes overriding area #

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

    def area(self):
        return 3.14159 * (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

class Triangle(Shape):
    def __init__(self, base, height):
        self.base = base
        self.height = height

    def area(self):
        return 0.5 * self.base * self.height

Step 3: Using polymorphism to process all shapes #

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

for shape in shapes:
    print(f"Area: {shape.area()}")

Output:

Area: 78.53975
Area: 24
Area: 10.5

You can call .area() on any shape and get the correct calculation without caring what shape it is.


Advantages of Polymorphism #

  • Cleaner Code: One interface, many implementations.
  • Easier Maintenance: Changes to child classes don’t affect callers.
  • Flexibility: Add new types without modifying existing code.
  • Supports Interfaces & Abstract Classes: Forces classes to implement required methods.

Summary of Key Concepts #

ConceptExplanationCode Example
PolymorphismSame method call, different behavioranimal.speak() in different subclasses
Method OverridingRedefining a parent class method in childChild class providing its own speak
Duck TypingNo inheritance needed, only method presenceObjects with fly() method
Operator OverloadingDefining operator behavior for custom objects__add__ method in class


Updated on June 9, 2025