Last Updated: January 3, 2026
Polymorphism is one of those powerful concepts in programming that can make your code more flexible and easier to manage. It allows objects of different classes to be treated as objects of a common superclass.
This means you can write code that works on the general type of an object, rather than needing to know the specific type. It’s a fundamental part of object-oriented programming (OOP) that helps promote code reusability and scalability.
Imagine you have different types of vehicles: cars, trucks, and motorcycles. Each of these vehicles has a method called start. With polymorphism, you could call this method on any vehicle object without worrying about the specific class it belongs to.
This makes your code cleaner and more maintainable, especially as your application grows.
At its core, polymorphism means "many shapes." In the context of programming, it allows methods to do different things based on the object it is acting upon. In Python, polymorphism is typically achieved through method overriding or through duck typing, which we will explore in more detail in the next chapter.
Polymorphism can be broadly categorized into two types:
Let's dive deeper into how runtime polymorphism works through method overriding and how it can be applied in real-world scenarios.
Method overriding is when a subclass provides a specific implementation of a method that is already defined in its superclass. This enables polymorphic behavior when you call a method on an instance of the subclass.
Consider a simple example with a base class Animal that has a method make_sound. We can create subclasses like Dog and Cat, each overriding the make_sound method.
In this example, the animal_sound function treats both Dog and Cat as Animal types. When you call make_sound, the appropriate implementation for the actual object type is invoked. This makes it easy to add new types in the future without changing the function that uses them.
Polymorphism shines in applications where you need to handle multiple types of objects uniformly. Let's look at a few real-world scenarios where polymorphism can simplify your code.
Imagine you have a payment processing system that handles different types of payments: credit cards, PayPal, and bank transfers. Instead of writing separate functions for each payment type, you can define a base class Payment and have specific payment methods override a method like process.
In this example, you can easily add new payment types by creating a new subclass of Payment. The process_payment function doesn't need to change, demonstrating the power of polymorphism in handling multiple implementations seamlessly.
One of the nuances of polymorphism in Python is understanding the Method Resolution Order (MRO). This determines the order in which base classes are searched when executing a method. It's particularly relevant in multiple inheritance scenarios.
Python uses the C3 linearization algorithm to determine the MRO, which ensures a consistent and predictable order. You can check the MRO of a class using the __mro__ attribute.
Consider the following example with multiple inheritance:
In this case, D inherits from both B and C. When you call d.greet(), Python first looks for greet in B, then C, and finally A. The MRO ensures that B's method is executed since it appears first.
Understanding MRO is essential when working with complex class hierarchies to avoid unexpected behavior.
When using polymorphism, there are a few best practices to keep in mind:
By mastering polymorphism, you can write more efficient, clean, and maintainable code that is easier to extend in the future.
Now that you understand the ins and outs of polymorphism, you are ready to explore duck typing.
In the next chapter, we will look at how duck typing differs from traditional polymorphism and how it can give you more flexibility in your code without the need for explicit inheritance.