Polymorphism means different objects can be used through the same operation when they provide the expected behavior.
In Python, the object does not need to inherit from a specific class if it has the method your code calls.
This keeps code flexible: write the loop once, and let each object decide how its own method works.
Polymorphism means "many forms." In Python, it allows different classes to be treated through the same interface - the same method name works differently depending on the object calling it.
class Dog:
def speak(self) -> str:
return "Woof!"
class Cat:
def speak(self) -> str:
return "Meow!"
class Duck:
def speak(self) -> str:
return "Quack!"
# Polymorphism - same interface, different behavior
animals = [Dog(), Cat(), Duck()]
for animal in animals:
print(animal.speak()) # Woof! / Meow! / Quack!
# Works with a function too
def make_sound(animal):
print(animal.speak()) # doesn't care about the type
make_sound(Dog()) # Woof!
make_sound(Cat()) # Meow!
Python uses "duck typing" - if an object has the right methods, it works, regardless of its class. "If it walks like a duck and quacks like a duck, it's a duck."
class TextFile:
def read(self) -> str:
return "Reading from text file"
class NetworkStream:
def read(self) -> str:
return "Reading from network"
class DatabaseCursor:
def read(self) -> str:
return "Reading from database"
# This function works with ANY object that has a read() method
def process(source):
data = source.read()
print(f"Got: {data}")
process(TextFile()) # Got: Reading from text file
process(NetworkStream()) # Got: Reading from network
process(DatabaseCursor()) # Got: Reading from database
# Built-in polymorphism
print(len("hello")) # 5 - works on strings
print(len([1, 2, 3])) # 3 - works on lists
print(len({"a": 1})) # 1 - works on dicts
Python lets you define how operators like +, -, *, == work on your custom classes using dunder methods.
class Vector:
def __init__(self, x: float, y: float):
self.x = x
self.y = y
def __add__(self, other: "Vector") -> "Vector":
return Vector(self.x + other.x, self.y + other.y)
def __sub__(self, other: "Vector") -> "Vector":
return Vector(self.x - other.x, self.y - other.y)
def __mul__(self, scalar: float) -> "Vector":
return Vector(self.x * scalar, self.y * scalar)
def __eq__(self, other: "Vector") -> bool:
return self.x == other.x and self.y == other.y
def __abs__(self) -> float:
import math
return math.sqrt(self.x**2 + self.y**2)
def __str__(self) -> str:
return f"Vector({self.x}, {self.y})"
v1 = Vector(1, 2)
v2 = Vector(3, 4)
print(v1 + v2) # Vector(4, 6)
print(v2 - v1) # Vector(2, 2)
print(v1 * 3) # Vector(3, 6)
print(abs(v2)) # 5.0
print(v1 == Vector(1, 2)) # True
Use ABC and @abstractmethod to define an interface that subclasses must implement.
from abc import ABC, abstractmethod
class Shape(ABC):
@abstractmethod
def area(self) -> float:
pass
@abstractmethod
def perimeter(self) -> float:
pass
def describe(self) -> str:
return f"Area: {self.area():.2f}, Perimeter: {self.perimeter():.2f}"
class Circle(Shape):
def __init__(self, radius: float):
self.radius = radius
def area(self) -> float:
import math
return math.pi * self.radius ** 2
def perimeter(self) -> float:
import math
return 2 * math.pi * self.radius
class Rectangle(Shape):
def __init__(self, w: float, h: float):
self.w = w
self.h = h
def area(self) -> float:
return self.w * self.h
def perimeter(self) -> float:
return 2 * (self.w + self.h)
shapes = [Circle(5), Rectangle(4, 6)]
for shape in shapes:
print(shape.describe())
# Shape() # TypeError - can't instantiate abstract class
Try this next
0 of 3 completed
No. Python often uses duck typing: if an object has the needed method, the code can use it.
A report exporter can call export() on PDF, CSV, and JSON objects without knowing their internal details.
Use clear method names, tests, and sometimes abstract base classes or protocols for larger projects.
Explore 500+ free tutorials across 20+ languages and frameworks.