Types of Polymorphism in Python

By Rahul Singh

Updated on Jun 10, 2026 | 10 min read | 4.38K+ views

Share:

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. 

What Is Polymorphism in Python?

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:

  • Through duck typing, which is Python's default and most flexible approach
  • Through method overriding in class inheritance
  • Through operator overloading using special methods
  • Through method overloading, which Python handles differently than other languages

Each of these is a form of polymorphism, and they all solve slightly different problems. Let us go through each one properly.

Quick Reference: All Types of Polymorphism in Python

Before we deep dive into the type of polymorphism in python here is a quick reference:

Duck Typing: Python's Native Polymorphism

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.

How Duck Typing Works

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)

Why Duck Typing Matters

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: Polymorphism Through Inheritance

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.

The Classic Example

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

When to Use Method Overriding

Use method overriding when:

  • You have a base class that defines a general behavior
  • Child classes need to customize that behavior
  • You want to iterate over mixed object types with one consistent interface

The Role of super()

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: Redefining What Operators Do

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.

How It Works

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.

Common Dunder Methods for Operator Overloading

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.

Practical Use Case

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

Method Overloading in Python: A Different Approach

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.

Using Default Arguments

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.

Using *args

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

Comparison: Python vs Other Languages

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.

Runtime vs Compile-Time Polymorphism in Python

This is a concept that comes up often when you are comparing Python to statically typed languages.

  • Compile-time polymorphism (also called static polymorphism) is resolved before the program runs. Method overloading in Java is a good example. The compiler knows at build time which version of a method to call.
  • Runtime polymorphism (also called dynamic polymorphism) is resolved while the program is running. Method overriding in Python works this way. Python looks up the method on the actual object at the moment the call happens.
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

Polymorphism With Abstract Classes

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

Conclusion

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.     

Frequently Asked Question (FAQs)

1. What are the main types of polymorphism in Python?

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.

2. Does Python support method overloading like Java?

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.

3. What is the difference between method overloading and method overriding in Python?

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.

4. What is duck typing in Python?

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.

5. Is polymorphism in Python static or dynamic?

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.

6. What are dunder methods and how do they relate to polymorphism?

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.

7. When should I use abstract classes for polymorphism in Python?

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.

8. Can polymorphism work without inheritance in Python?

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.

9. How does Python decide which method to call during method overriding?

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.

10. What is the practical benefit of using polymorphism in Python projects?

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.

11. How is polymorphism different from abstraction in Python?

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.

Rahul Singh

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

View Program