Polymorphism in Python: Complete Guide
How Python implements polymorphism through duck typing, method overriding, operator overloading, and abstract base classes, with code examples for technical interviews.
Polymorphism lets one function or operator work on many types, and Python is more permissive about this than most compiled languages.
What Polymorphism Is (and Why Python Handles It Differently)
The word comes from Greek: polys (many) + morphe (form). In programming, it means one interface, many behaviours.
Java and C++ enforce polymorphism through class hierarchies and interface declarations. Python’s default approach is looser. The Python interpreter doesn’t ask “is this object a subclass of X?” It asks “does this object have the method I’m about to call?” If yes, it calls it. No ancestry check required.
This makes Python code shorter and more flexible, but it also shifts the responsibility for correctness onto the developer.
Python expresses polymorphism in four ways:
- Built-in function polymorphism (e.g.,
len()) - Duck typing with user-defined classes
- Method overriding through inheritance
- Operator overloading through dunder methods
One enforcement mechanism sits alongside all four: Abstract Base Classes from the abc module.
Built-In Polymorphism: The len() Example
len() is probably the most-cited example in Python placement interviews, and for good reason.
# len() works on multiple types without any conditional logic
print(len([2, 4, 6, 8, 9])) # 5
print(len("polymorphism")) # 12
print(len({"a": 1, "b": 2})) # 2
print(len((10, 20, 30))) # 3
The function signature doesn’t change. What changes is what len() does internally, depending on the type of object it receives. Under the hood, calling len(obj) calls obj.__len__(). Every built-in type implements __len__ differently. Define __len__ in your own class and len() will work on your objects too.
This is the simplest form of polymorphism and the one interviewers use as a warm-up question.
Duck Typing: Python’s Primary Polymorphism Mechanism
“If it walks like a duck and quacks like a duck, it’s a duck.” Python doesn’t care about class names. It cares about whether the object has the method you’re calling.
class Rectangle:
def __init__(self, length, breadth):
self.length = length
self.breadth = breadth
def calculate_area(self):
return self.length * self.breadth
class Circle:
def __init__(self, radius):
self.radius = radius
def calculate_area(self):
return 3.14159 * self.radius ** 2
shapes = [Rectangle(5, 3), Circle(4)]
for shape in shapes:
print(shape.calculate_area())
# Output:
# 15
# 50.265...
Rectangle and Circle share no parent class. The loop calls calculate_area() on both without any type check. That’s duck typing: the method name is the contract, not the class hierarchy.
The practical risk: pass an object without calculate_area() into that loop and Python raises AttributeError at runtime. Abstract Base Classes (covered below) let you catch this at class-definition time instead.
Technical interview note: if an interviewer asks “how does Python achieve polymorphism without interface keywords?”, duck typing is the answer.
Method Overriding with Inheritance
Method overriding is the classical OOP approach. A child class redefines a method from the parent class, and Python calls the correct version based on the object’s actual type.
class Vehicle:
def describe(self):
return "I am a vehicle."
class Car(Vehicle):
def describe(self):
return "I am a car with 4 wheels."
class Motorcycle(Vehicle):
def describe(self):
return "I am a motorcycle with 2 wheels."
fleet = [Vehicle(), Car(), Motorcycle()]
for v in fleet:
print(v.describe())
# Output:
# I am a vehicle.
# I am a car with 4 wheels.
# I am a motorcycle with 2 wheels.
The describe() call dispatches to the right method at runtime. Python resolves this through the Method Resolution Order (MRO), which follows the C3 linearisation algorithm. For simple single-inheritance hierarchies, MRO means “child’s method first, parent’s method as fallback.”
When the child class calls super().describe(), it explicitly invokes the parent’s version. This matters in deep hierarchies where partial overriding is needed.
For a related example of a single function approach working cleanly across multiple input types, the palindrome check article covers the same “one algorithm, multiple language implementations” pattern in string processing.
Operator Overloading: Dunder Methods
Python’s operators (+, ==, <, str()) all call dunder methods on the objects involved. Python’s data model reference lists all of them.
Define those methods in your class and the operators work on your objects.
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})"
def __eq__(self, other):
return self.x == other.x and self.y == other.y
v1 = Vector(2, 3)
v2 = Vector(1, 4)
print(v1 + v2) # Vector(3, 7)
print(v1 == v2) # False
The dunder methods that appear most in placement technical rounds:
| Method | Operator | Triggered by |
|---|---|---|
__add__ | + | a + b |
__sub__ | - | a - b |
__mul__ | * | a * b |
__str__ | string conversion | str(obj) or print(obj) |
__len__ | length | len(obj) |
__eq__ | equality | a == b |
__lt__ | comparison | a < b |
__str__ deserves special attention. Without it, print(v1) outputs something like <__main__.Vector object at 0x10f3a2d10>. With it, you control how any object appears in output, logs, and error messages. Interviewers often ask students to add __str__ to a class they’ve just written and explain what changes.
Abstract Base Classes: Enforcing the Contract
Duck typing defers errors to runtime. Abstract Base Classes (ABCs) from Python’s abc module let you declare which methods a subclass must implement, and Python raises TypeError at instantiation if the contract is broken.
from abc import ABC, abstractmethod
class Shape(ABC):
@abstractmethod
def area(self):
pass
@abstractmethod
def perimeter(self):
pass
class Rectangle(Shape):
def __init__(self, width, height):
self.width = width
self.height = height
def area(self):
return self.width * self.height
def perimeter(self):
return 2 * (self.width + self.height)
class Triangle(Shape):
def __init__(self, base, height, side_a, side_b):
self.base = base
self.height = height
self.side_a = side_a
self.side_b = side_b
def area(self):
return 0.5 * self.base * self.height
def perimeter(self):
return self.base + self.side_a + self.side_b
# A class that skips the contract raises TypeError at instantiation:
# class BadShape(Shape):
# pass
# s = BadShape() # TypeError: Can't instantiate abstract class BadShape
# # without abstract methods area, perimeter
Shape is an interface contract. Any class that inherits from it must implement both area() and perimeter(). Miss one and Python raises TypeError the moment you try to instantiate the class, not when you first call the missing method. That earlier error surface is the main practical reason to use ABCs over pure duck typing in a production codebase.
ABCs also show up in Python’s standard library. collections.abc defines abstract classes like Iterable, Mapping, and Sequence that built-in types already satisfy. Checking isinstance(obj, Iterable) is a clean way to test whether an object can be looped over.
For an adjacent set of technical questions where OOP and data structure knowledge intersect, see the 20 most asked DSA interview questions. OOP polymorphism and DSA questions often appear in the same technical round at mid-tier IT companies.
A Practical Note on Placement Interviews
Interviewers testing OOP in Python will typically ask one of three things:
- “What is the difference between duck typing and interface-based polymorphism?” Answer: duck typing checks method presence at call time; ABCs enforce the contract at class-definition time.
- “Can you implement a class that behaves like a list?” Hint: implement
__len__,__getitem__, and__iter__. - “What does Python’s
super()call do in the context of method overriding?” It invokes the parent class’s version of the method, enabling cooperative multiple inheritance through MRO.
Working through an array problem first? The smallest and largest element search covers the iterative vs. recursive distinction that often appears in the same technical round as OOP questions.
Duck typing, method overriding, operator overloading, and ABCs are four distinct mechanisms. Understanding when to use each one separates candidates who have read about OOP from those who have applied it.
Python’s dunder method system applies directly to LLM pipelines: a handler that processes str, list, or dict input through the same call signature is duck typing at work. TinkerLLM’s Python environment at ₹299 is a good place to test that pattern against real API calls rather than toy examples.
Primary sources
Frequently asked questions
Does Python support method overloading?
Not in the traditional sense. Python doesn't allow two methods with the same name but different parameter types in the same class. Duck typing and default arguments handle the same use cases more flexibly.
What is the difference between method overriding and method overloading?
Method overriding redefines an inherited method in a subclass, so the child's version runs instead of the parent's. Method overloading (as in Java or C++) means multiple methods with the same name but different parameter signatures — Python doesn't support this natively.
What is duck typing in Python?
Duck typing means Python checks for the presence of a method at call time, not the class ancestry of the object. If an object has a `.speak()` method, you can call it — Python doesn't care whether the object inherits from an Animal class.
When should I use abstract base classes instead of duck typing?
Use abstract base classes when you need to enforce a strict interface contract across a hierarchy. ABCs raise TypeError at class instantiation if a required method is missing, which catches errors earlier than duck typing would.
How does operator overloading work in Python?
Python calls a dunder method on the left operand when it encounters an operator. For `a + b`, Python calls `a.__add__(b)`. Define `__add__` in your class to control what + does for your objects.
Can polymorphism exist without inheritance in Python?
Yes. Duck typing enables polymorphism without any inheritance relationship. Any two classes that implement the same method name can be used interchangeably in code that calls that method — no shared parent class required.
A self-paced playground for building with LLMs.
TinkerLLM is FACE Prep's sister property. A guided environment for shipping real LLM applications, the kind of project that earns a paragraph on your resume, not a line.
Try TinkerLLM (₹299 launch)