Types of Polymorphism in Python
By Rahul Singh
Updated on Jun 10, 2026 | 10 min read | 4.38K+ views
Share:
Looks like you're browsing from the
United StatesSome programs may not be available in your location
Some programs may not be available in your location
Switch to upGrad USAll courses
Certifications
More
By Rahul Singh
Updated on Jun 10, 2026 | 10 min read | 4.38K+ views
Share:
Table of Contents
The types of polymorphism in Python allow the same method, function, or operator to behave differently depending on the object, data type, or context in which it is used. Python supports several forms of polymorphism, including duck typing, function polymorphism, method overriding, and operator overloading, each helping developers write flexible and reusable code.
Understanding these types of polymorphism is essential for mastering object-oriented programming in Python.
In this blog, you will learn all the major types of polymorphism in Python with real code examples and plain explanations. Whether you are just starting out or you already write Python regularly, this guide is structured to build your understanding step by step.
Build practical AI and ML skills with upGrad’s Artificial Intelligence Courses. Learn machine learning, generative AI, and emerging technologies while working on real-world projects.
Before jumping into the types, it helps to understand what polymorphism actually does for you as a developer.
Imagine you have a function called make_sound(). You want it to work for a Dog, a Cat, and a Parrot without writing three separate functions. Polymorphism lets you do exactly that. The same call, different behavior depending on the object.
Python supports polymorphism in a few different ways:
Each of these is a form of polymorphism, and they all solve slightly different problems. Let us go through each one properly.
Before we deep dive into the type of polymorphism in python here is a quick reference:
Duck typing is the most Python-specific form of polymorphism. The name comes from the saying: "If it walks like a duck and quacks like a duck, it is a duck."
In Python, you do not need objects to share a class or even an interface. You just need them to have the right method or attribute. Python checks at runtime whether the object can do what you are asking, not whether it is a specific type.
class Dog:
def speak(self):
return "Woof!"
class Cat:
def speak(self):
return "Meow!"
class Parrot:
def speak(self):
return "Hello!"
def make_sound(animal):
print(animal.speak())
make_sound(Dog()) # Output: Woof!
make_sound(Cat()) # Output: Meow!
make_sound(Parrot()) # Output: Hello!
The make_sound() function does not care what type animal is. It just calls .speak(). As long as the object has that method, Python is happy.
Also Read: A Complete Guide To Method Overloading in Python (With examples)
Feature |
Duck Typing in Python |
| Type check | At runtime, not at compile time |
| Inheritance required | No |
| Flexibility | Very high |
| Common use | Functions working with multiple object types |
Duck typing is why Python code tends to be shorter and more flexible than Java or C++. You write what you need, not what the type system demands.
One thing to watch: If an object does not have the expected method, Python raises an AttributeError at runtime. So test your code with all the object types you plan to use.
Method overriding is what most people picture when they hear about polymorphism in OOP. It happens when a child class provides its own version of a method that already exists in the parent class.
class Shape:
def area(self):
return 0
class Circle(Shape):
def __init__(self, radius):
self.radius = radius
def area(self):
return 3.14 * self.radius * self.radius
class Rectangle(Shape):
def __init__(self, width, height):
self.width = width
self.height = height
def area(self):
return self.width * self.height
shapes = [Circle(5), Rectangle(4, 6)]
for shape in shapes:
print(shape.area())
Output:
78.5
24
Both Circle and Rectangle inherit from Shape. Both override the area() method. When you loop through the list and call .area(), each object uses its own version.
Also Read: Understanding Type Function in Python
Use method overriding when:
Sometimes a child class does not want to completely replace the parent method. It wants to extend it. That is where super() comes in.
class Animal:
def describe(self):
print("I am an animal.")
class Dog(Animal):
def describe(self):
super().describe()
print("I am also a dog.")
Dog().describe()
# Output:
# I am an animal.
# I am also a dog.
super() calls the parent class method first, then adds the child's behavior. This is a clean pattern when you need both.
Also Read: Top 36+ Python Projects for Beginners in 2026
Operator overloading lets you define how Python's built-in operators (+, -, *, ==, etc.) behave when used with your own objects.
This is a form of polymorphism because the same operator symbol produces different results depending on the type of object it is working with.
Python uses special methods (also called dunder methods, short for double underscore) to link operators to behavior.
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
def __add__(self, other):
return Vector(self.x + other.x, self.y + other.y)
def __str__(self):
return f"Vector({self.x}, {self.y})"
v1 = Vector(2, 3)
v2 = Vector(4, 1)
print(v1 + v2) # Output: Vector(6, 4)
When Python sees v1 + v2, it calls v1.__add__(v2) behind the scenes. You have full control over what that does.
Operator |
Dunder Method |
Example |
| + | __add__ | a + b |
| - | __sub__ | a - b |
| * | __mul__ | a * b |
| == | __eq__ | a == b |
| < | __lt__ | a < b |
| str() | __str__ | print(a) |
| len() | __len__ | len(a) |
You have probably used operator overloading without realizing it. When you do "Hello" + " World", Python calls the __add__ method on the string class.
Operator overloading is most useful when you are building custom classes that represent mathematical or logical concepts, such as matrices, vectors, fractions, or money values.
Also Read: 12 Incredible Python Applications You Should Know About
In languages like Java or C++, method overloading means defining the same method multiple times with different parameter types or counts. Python does not support that natively. Define a method twice, and the second one simply replaces the first.
But Python gives you tools to achieve the same result.
class Calculator:
def add(self, a, b=0, c=0):
return a + b + c
calc = Calculator()
print(calc.add(5)) # Output: 5
print(calc.add(5, 3)) # Output: 8
print(calc.add(5, 3, 2)) # Output: 10
By giving parameters default values, you make one method handle multiple call signatures.
class Calculator:
def add(self, *args):
return sum(args)
calc = Calculator()
print(calc.add(1, 2)) # Output: 3
print(calc.add(1, 2, 3, 4)) # Output: 10
*args collects all positional arguments into a tuple, so your method handles any number of inputs.
Also Read: Python Libraries Explained: List of Important Libraries
Feature |
Python |
Java / C++ |
| Native overloading | No | Yes |
| Workaround | Default args, *args, **kwargs | Multiple method signatures |
| Flexibility | High | Structured |
| Code simplicity | More concise | More explicit |
Python's approach is less formal but often cleaner in practice.
This is a concept that comes up often when you are comparing Python to statically typed languages.
class Bird:
def fly(self):
print("Bird is flying.")
class Penguin(Bird):
def fly(self):
print("Penguins cannot fly.")
def let_it_fly(bird):
bird.fly()
let_it_fly(Bird()) # Bird is flying.
let_it_fly(Penguin()) # Penguins cannot fly.
Python decides which fly() to call at runtime, based on the actual type of the object passed in. This is why Python is considered a dynamically typed language, and it is what makes method overriding in Python a form of runtime polymorphism.
Most polymorphism in Python is runtime-based. That is a deliberate design choice, and it is why Python code stays flexible without requiring complex type hierarchies.
Also Read: A Complete Guide on OOPs Concepts in Python
Abstract classes push polymorphism one step further. They define a contract: any class that inherits from an abstract class must implement certain methods. If it does not, Python raises an error when you try to create an instance.
Python provides this through the abc module.
from abc import ABC, abstractmethod
class Payment(ABC):
@abstractmethod
def process(self, amount):
pass
class CreditCard(Payment):
def process(self, amount):
print(f"Processing credit card payment of {amount}")
class UPI(Payment):
def process(self, amount):
print(f"Processing UPI payment of {amount}")
payments = [CreditCard(), UPI()]
for p in payments:
p.process(500)
Output:
Processing credit card payment of 500
Processing UPI payment of 500
Abstract classes are a cleaner way to enforce polymorphism in larger codebases. They make it explicit that a method must be overridden, and they prevent incomplete implementations from being used by mistake.
Also Read: Python Cheat Sheet: From Fundamentals to Advanced Concepts for 2025
Polymorphism is not one thing in Python. It shows up in different ways depending on what you are building and how your code is structured.
Understanding the types of polymorphism in Python gives you better tools to write cleaner, more reusable code. You stop writing the same logic multiple times and start writing code that adapts to what it receives.
Want personalized guidance on AI and upskilling? Speak with an expert for a free 1:1 counselling session today.
The main types of polymorphism in Python are duck typing, method overriding, operator overloading, and method overloading (achieved via default arguments or *args). Python also supports polymorphism through abstract base classes, which enforce method implementation across subclasses.
Python does not support traditional method overloading where you define the same method multiple times with different parameters. Instead, Python uses default argument values or *args and **kwargs to achieve similar behavior in a single method definition.
Method overriding happens when a child class redefines a method from its parent class. Method overloading refers to a single method handling multiple input signatures. Python supports overriding natively through inheritance, while overloading is handled using default or variable arguments.
Duck typing is a Python concept where the type of an object is less important than the methods it has. If an object has the method you are calling, Python will work with it regardless of its class. It makes Python code flexible and reduces the need for strict type checks.
Most polymorphism in Python is dynamic, meaning it is resolved at runtime. Method overriding is a clear example where Python decides which version of a method to call based on the actual type of the object during execution, not before the program runs.
Dunder methods (double underscore methods like __add__, __str__, __eq__) allow you to define how built-in Python operators and functions behave with your custom objects. This is operator overloading, which is a type of polymorphism that lets the same operator symbol work differently for different object types.
Use abstract classes when you want to enforce that every subclass implements specific methods. This is useful in larger projects where multiple developers or teams are writing subclasses. The abc module in Python provides the ABC class and @abstractmethod decorator for this purpose.
Yes. Duck typing is a form of polymorphism that works without any inheritance. As long as two objects have the same method or attribute, a function can work with both of them without requiring any shared parent class.
Python uses the Method Resolution Order (MRO) to determine which method to call. When you call a method on an object, Python starts at the object's class and works up the inheritance chain. The first class in that chain that defines the method is the one Python uses.
Polymorphism reduces code duplication and makes systems easier to extend. When you add a new class that follows an existing interface, existing functions and loops work with it automatically without any changes. This is the open/closed principle in practice: open for extension, closed for modification.
Abstraction hides complex implementation details and exposes only what is necessary. Polymorphism allows different objects to respond to the same method call in their own way. Both are OOP principles, but abstraction is about simplifying interfaces while polymorphism is about flexible, type-independent behavior.
60 articles published
Rahul Singh is an Associate Content Writer at upGrad, with a strong interest in Data Science, Machine Learning, and Artificial Intelligence. He combines technical development skills with data-driven s...
India’s #1 Tech University
Executive Program in Generative AI for Leaders
76%
seats filled