Skip to main content

Concept

Polymorphism is the ability to use a single interface to represent different underlying data types. It allows methods to do different things based on the object they are acting upon — methods can have the same name but behave differently based on the object type.
TypeDescription
Compile-time Polymorphism (Method/Operator Overloading)The method to be executed is determined at compile time. Allows the same method name to be used with different parameters or types.
Runtime Polymorphism (Method Overriding)The method to be executed is determined at runtime. Allows a subclass to provide a specific implementation of a method already defined in its superclass.

Implementation

Method Overriding

The area method is overridden in Circle and Rectangle to provide shape-specific implementations. The Shape class serves as a base class with a generic area method.
shapes.py
class Shape:
  def area(self):
    pass

class Circle(Shape):
  def __init__(self, radius):
    self.radius = radius

  def area(self):
    return 3.14 * self.radius ** 2

class Rectangle(Shape):
  def __init__(self, length, width):
    self.length = length
    self.width = width

  def area(self):
    return self.length * self.width

# Runtime Polymorphism
shapes = [Circle(5), Rectangle(4, 6)]
for shape in shapes:
  print(f"Area: {shape.area()}")  # Calls the overridden method based on object type

Method Overloading

Python does not natively support method overloading (the last definition wins), but you can simulate it using default arguments or *args.
calculator.py
class Calculator:
    def add(self, a, b):
      return a + b
    def add(self, a, b, c):
      return a + b + c

calculator = Calculator()
print(calculator.add(2, 3))      # Output: 5
print(calculator.add(2, 3, 4))   # Output: 9

Operator Overloading

Methods like __add__, __str__, __eq__, etc. are called dunder methods (double underscore methods) or magic methods. They allow you to define how operators and built-in functions behave for user-defined classes.
Operator overloading lets you define how operators (like +, ==) work for custom classes by implementing the corresponding dunder method.
vector.py
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

vector1 = Vector(1, 2)
vector2 = Vector(3, 4)
result = vector1 + vector2    # Uses __add__

print(result)                 # Output: Vector(4, 6)
print(vector1 == vector2)     # Uses __eq__, Output: False
Dunder MethodOperatorDescription
__add__+Addition
__sub__-Subtraction
__mul__*Multiplication
__eq__==Equality check
__str__str()String representation
__len__len()Length

Build docs developers (and LLMs) love