Skip to main content

Polymorphism: Method Overriding and Duck Typing

We've learned about inheritance and how a child class can inherit attributes and methods from a parent class. Now we're going to explore polymorphism, a powerful concept that allows us to treat objects of different classes in a similar way.

The word "polymorphism" comes from Greek and means "many forms." In programming, it means that a single interface (like a method name) can represent different underlying forms (data types or classes). In Python, polymorphism is primarily achieved through two mechanisms: method overriding (which is tied to inheritance) and duck typing.


πŸ“š Prerequisites​

You should understand inheritance and how to create a parent and child class.


🎯 Article Outline: What You'll Master​

In this article, you will learn:

  • βœ… The Concept of Polymorphism: Understand what it means for different objects to respond to the same message (method call).
  • βœ… Polymorphism with Inheritance: See how method overriding allows different subclasses to have unique behaviors for the same method.
  • βœ… Duck Typing: Learn about Python's dynamic approach to polymorphism, where an object's suitability is determined by its methods, not its class type.
  • βœ… The Difference: Understand when each concept applies.

🧠 Section 1: Polymorphism with Class Inheritance​

We've already seen the foundation of this in the last article on inheritance. When a child class provides its own implementation of a method that it also inherits from a parent class, that's method overriding. This is a form of polymorphism.

Let's create a few different Animal types. They all inherit from a base Animal class, but each one has a different way of "speaking."

# polymorphism_inheritance.py

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

def speak(self):
# A generic placeholder
raise NotImplementedError("Subclass must implement this abstract method")

class Dog(Animal):
def speak(self):
return "Woof!"

class Cat(Animal):
def speak(self):
return "Meow!"

class Duck(Animal):
def speak(self):
return "Quack!"

# --- Let's create a list of different animal objects ---
animals = [Dog("Buddy"), Cat("Whiskers"), Duck("Daffy")]

# --- Now, let's interact with them polymorphically ---
for animal in animals:
# We don't need to know if the animal is a Dog, Cat, or Duck.
# We just know that it's an Animal and that it can .speak().
print(f"{animal.name} says: {animal.speak()}")

Output:

Buddy says: Woof!
Whiskers says: Meow!
Daffy says: Quack!

This is polymorphism in action. We have a single interfaceβ€”the for loopβ€”that treats all the objects the same way (animal.speak()). However, the actual behavior that gets executed is different for each object, depending on its class. The program doesn't have to check if isinstance(animal, Dog): ... because it trusts that any Animal object will have a speak method.


πŸ’» Section 2: Duck Typing - If it Quacks Like a Duck...​

Python is a dynamically typed language, which leads to a more flexible and powerful form of polymorphism known as duck typing.

The name comes from the phrase:

"If it looks like a duck, swims like a duck, and quacks like a duck, then it probably is a duck."

In programming terms, this means: an object's type is less important than the methods it defines. If an object has the methods and attributes necessary to perform a certain task, it can be used for that task, regardless of what class it is or what it inherits from.

Let's revisit our previous example, but this time with an object that has no relationship to the Animal class.

# duck_typing_example.py

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

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

# This class has NO inheritance relationship with Dog or Cat
class Car:
def speak(self):
return "Vroom!"

def make_it_speak(some_object):
"""This function will call the .speak() method on any object passed to it."""
print(some_object.speak())


# --- Let's create our objects ---
my_dog = Dog()
my_cat = Cat()
my_car = Car()

# --- Let's pass them to the function ---
make_it_speak(my_dog)
make_it_speak(my_cat)
make_it_speak(my_car) # This works perfectly!

Output:

Woof!
Meow!
Vroom!

The make_it_speak function doesn't care about the type of some_object. It only cares that the object has a .speak() method. Because the Car object has a .speak() method, it "quacks like a duck," and the function accepts it. This is duck typing. It allows for incredible flexibility and decouples our code from strict class hierarchies.


✨ Conclusion & Key Takeaways​

Polymorphism is a core OOP principle that allows you to write more generic and flexible code. By programming to a common interface (like the speak method) instead of a specific class type, you can create systems that are easier to extend and maintain.

Let's summarize the key takeaways:

  • Polymorphism means "many forms." It allows different objects to respond to the same method call in their own unique ways.
  • Method Overriding is polymorphism in the context of inheritance. Subclasses provide specific implementations of a parent class's methods.
  • Duck Typing is a more general form of polymorphism in Python. It focuses on an object's behavior (the methods it has) rather than its type (its class).
  • This flexibility allows you to write functions that can operate on a wide range of objects, as long as they support the required operations.

➑️ Next Steps​

We've now covered the three major pillars of OOP: Encapsulation, Inheritance, and Polymorphism. Next, we'll explore "Abstraction," a concept that helps us manage complexity by hiding unnecessary details and exposing only the essential features of an object.

Happy coding!