Understanding Polymorphism in Python
Polymorphism means that different objects can respond to the same operation in their own way. In Python, this often appears through inheritance, method overriding, protocols, and duck typing.
Method Overriding with Inheritance
class Animal:
def sound(self):
return "unknown sound"
class Dog(Animal):
def sound(self):
return "bark"
class Cat(Animal):
def sound(self):
return "meow"Code can treat each object through the shared sound() interface:
animals = [Dog(), Cat()]
for animal in animals:
print(animal.sound())Duck Typing
Python does not require related classes for many polymorphic operations. If an object supports the method the code needs, it can often be used:
class Alarm:
def sound(self):
return "beep"
class Dog:
def sound(self):
return "bark"
def play_sound(obj):
print(obj.sound())
play_sound(Alarm())
play_sound(Dog())This style is often summarized as “if it behaves like the required object, use it.”
Built-in Polymorphism
The same built-in operation can work with different types:
print(len("hello"))
print(len([1, 2, 3]))
print(len({"a": 1, "b": 2}))Each type defines what length means for that object.
Operators are polymorphic as well:
print(1 + 2)
print("hello " + "world")
print([1, 2] + [3, 4])Protocols for Static Type Checking
When using type hints, typing.Protocol can describe required behavior without forcing inheritance:
from typing import Protocol
class SupportsSound(Protocol):
def sound(self) -> str: ...
def play_sound(obj: SupportsSound) -> None:
print(obj.sound())Any compatible object can satisfy the protocol structurally.
Abstract Base Classes
If you need an explicit inheritance contract, the abc module can define abstract methods:
from abc import ABC, abstractmethod
class Animal(ABC):
@abstractmethod
def sound(self) -> str:
raise NotImplementedErrorSubclasses must implement the abstract method before they can be instantiated.
Conclusion
Python supports polymorphism through both explicit inheritance and flexible duck typing. Prefer the lightest abstraction that clearly communicates the required behavior: ordinary methods for simple cases, protocols for structural typing, and abstract base classes when an explicit hierarchy is valuable.