Hi! Today we will talk about a very important and interesting topic in Python (and many other programming languages): Polymorphism. Don’t let the fancy word intimidate you; we’re going to break it down piece by piece. By the end of this article, you’ll be able to recognize and apply polymorphism in your own code with ease. Ready? Let’s get started!

What is Polymorphism?

At its core, polymorphism means “many forms.” In programming, it refers to the ability to use a single interface or function in different ways. Think of it like a multi-tool: one tool, multiple uses. In Python, polymorphism allows objects of different classes to be treated as if they are of the same class. This flexibility helps us write code that is cleaner, easier to manage, and more adaptable.

There are different types of polymorphism in Python, and we’re going to cover the most common ones:

  • Function Polymorphism – Using functions in multiple ways
  • Class Polymorphism – Using methods across different classes
  • Polymorphism and Inheritance – How inheritance brings polymorphism into play

1. Function Polymorphism

In Python, functions are naturally polymorphic. This means that we can use the same function in multiple ways. Let’s look at a simple example to see this in action.

Example: Built-in Functions

Python’s built-in functions like len() demonstrate function polymorphism. The len() function can be used on different data types, such as strings, lists, and dictionaries. Despite being different types, len() works on all of them.

# Using len() on a string
text = "Hello, World!"
print(len(text))  # Output: 13

# Using len() on a list
numbers = [1, 2, 3, 4, 5]
print(len(numbers))  # Output: 5

# Using len() on a dictionary
data = {'name': 'Alice', 'age': 30}
print(len(data))  # Output: 2
  

Here, the len() function adapts to the type of object it’s working with, returning the length of strings, lists, and dictionaries appropriately. Isn’t that neat?

2. Class Polymorphism

Now let’s move on to class polymorphism. This is where polymorphism really shines, as it allows us to use the same method names across different classes. Each class can have its own version of the method, tailored to its specific needs.

Example: Shape Classes

Imagine we have different shapes, each with a method called area(). Each shape class implements its own version of the area() method, which calculates the area differently depending on the shape.

class Rectangle:
    def __init__(self, width, height):
        self.width = width
        self.height = height
        
    def area(self):
        return self.width * self.height

class Circle:
    def __init__(self, radius):
        self.radius = radius
        
    def area(self):
        return 3.14159 * (self.radius ** 2)

# Creating instances
rectangle = Rectangle(4, 5)
circle = Circle(3)

# Calling the area method on both shapes
print("Rectangle area:", rectangle.area())  # Output: 20
print("Circle area:", circle.area())        # Output: 28.2735
  

Here, both the Rectangle and Circle classes have an area() method. When we call area() on each instance, Python knows to use the version of area() specific to that class. This allows us to use the same interface (method name) in different ways, depending on the object.

3. Polymorphism and Inheritance

In Python, inheritance is a great way to achieve polymorphism. By creating a base class with a method that gets overridden in derived classes, we can achieve polymorphism through inheritance.

Example: Animal Classes

Let’s create a base class called Animal with a method called make_sound(). Then, we’ll create subclasses Dog and Cat that override make_sound().

class Animal:
    def make_sound(self):
        raise NotImplementedError("Subclasses must implement this method")

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

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

# Creating instances
dog = Dog()
cat = Cat()

# Calling the make_sound method on both animals
print(dog.make_sound())  # Output: Woof!
print(cat.make_sound())  # Output: Meow!
  

Here, make_sound() is defined in the Animal base class, but each subclass implements its own version. When we call make_sound() on a Dog or Cat instance, Python calls the appropriate method based on the object’s type. This is polymorphism in action!

Why is Polymorphism Useful?

Polymorphism helps us write flexible and reusable code. By allowing the same interface (like a method name) to perform different actions based on the object it’s called on, polymorphism lets us work with multiple data types or objects without changing the structure of our code.

Let’s look at a practical example to illustrate this point:

Example: Using Polymorphism to Calculate Total Area

Suppose we want to calculate the total area of different shapes. With polymorphism, we can achieve this without worrying about the specific types of shapes.

def calculate_total_area(shapes):
    total_area = 0
    for shape in shapes:
        total_area += shape.area()
    return total_area

# Creating a list of shapes
shapes = [Rectangle(4, 5), Circle(3)]
print("Total area:", calculate_total_area(shapes))  # Output: 48.2735
  

Here, we use a single function, calculate_total_area(), to find the total area of a list of shapes. Thanks to polymorphism, we don’t have to write separate code for each shape type. The function calls area() on each shape object, regardless of its class, allowing us to work with all shapes in the same way. How efficient is that?

Summary and Conclusion

In this article, we explored polymorphism in Python, breaking it down into different types:

  • Function Polymorphism – using the same function for different data types.
  • Class Polymorphism – implementing the same method in multiple classes.
  • Polymorphism and Inheritance – achieving polymorphism with a base class and overridden methods in subclasses.

Polymorphism is a powerful feature that makes your code more adaptable and easier to maintain. By understanding and implementing polymorphism, you’re taking another step toward mastering Python and building robust, efficient applications.