← Back to Blog

Polymorphism: When the Same Call Does Different Things

The problem...

You have a list of animals — dogs, cats, maybe other species. You want to call describe() on each one. But Dog.describe() and Cat.describe() produce different output. Do you need to check the type of each animal before calling the right method?

for animal in shelter:
    if isinstance(animal, Dog):
        animal.dog_describe()
    elif isinstance(animal, Cat):
        animal.cat_describe()

That's not scalable. Every new species means a new condition. And it defeats the purpose of having a shared interface.

The idea!

Polymorphism means that the same method call — animal.describe() — produces different behavior depending on the type of object it's called on. You don't need to know what kind of animal it is. You just call describe() and let the object handle it.

Making it real

class Animal:
    def __init__(self, name, age):
        self.name = name
        self.age  = age

    def describe(self):
        print(f"{self.name} is {self.age} years old.")

class Dog(Animal):
    def __init__(self, name, age, breed):
        super().__init__(name, age)
        self.breed = breed

    def describe(self):
        print(f"{self.name} is a {self.age} year old {self.breed}.")

class Cat(Animal):
    def __init__(self, name, age, indoor):
        super().__init__(name, age)
        self.indoor = indoor

    def describe(self):
        location = "indoor" if self.indoor else "outdoor"
        print(f"{self.name} is a {self.age} year old {location} cat.")
shelter = [
    Dog("Lassie",   4, "Collie"),
    Cat("Whiskers", 2, True),
    Dog("Rex",      6, "Husky"),
    Cat("Luna",     3, False)
]

for animal in shelter:
    animal.describe()

Output:

Lassie is a 4 year old Collie.
Whiskers is a 2 year old indoor cat.
Rex is a 6 year old Husky.
Luna is a 3 year old outdoor cat.

One loop. One method call. Four different outputs. No isinstance() checks. No conditionals. Each object handles describe() in its own way.

Why this works

Python looks up the method on the object's actual class — not the parent class. When you call animal.describe() on a Dog object, Python finds Dog.describe() and calls that. When it's a Cat, it finds Cat.describe(). The same call routes to the right implementation automatically.

Polymorphism without inheritance

Python's polymorphism doesn't require inheritance. Any two classes that define the same method name can be used interchangeably — as long as you only call that method.

class Dog:
    def speak(self):
        print("Woof!")

class Cat:
    def speak(self):
        print("Meow!")

class Parrot:
    def speak(self):
        print("Squawk!")

animals = [Dog(), Cat(), Parrot()]
for animal in animals:
    animal.speak()

Output:

Woof!
Meow!
Squawk!

No shared parent. No inheritance. Just a shared method name — and Python calls the right one for each object.

A practical example

In the Safe Paws project, a report function could call describe() on any animal without caring about its type:

def print_shelter_report(animals):
    for animal in animals:
        animal.describe()    # works for Dog, Cat, or any future species

Add a Parrot class tomorrow with its own describe() — the report function doesn't change. It just works.

Heads up!

  • Polymorphism works because Python looks up the method on the object's actual class
  • No isinstance() checks needed — the object knows what to do
  • All classes in a polymorphic group must define the same method name
  • Python's polymorphism doesn't require inheritance — just a shared interface

The mindset shift

Stop thinking: "I need to check what type this object is before calling the right method."

Start thinking: "Every object knows how to handle this call. I just call it."

What you should understand now

  • Polymorphism means the same method call behaves differently depending on the object
  • Python routes the call to the correct class automatically
  • No type-checking needed — the object handles it
  • Polymorphism doesn't require inheritance — just a shared method name
  • New classes can be added without changing existing code that calls the method
[ login to bookmark ] // copied! 32 views · 2 min
// resources
Code Example oop_polymorphism.py
← prev Reusing Code with Inheritance next → Making Objects Behave Like Built-ins
// 0 comments
// No comments yet. Be the first.
// leave a comment

// Your comment will appear after approval.