Skip to content

Operator Overloading

One of the most powerful features of Python is that operators like +, -, and * are not “hard-wired” into the language. They are implemented through Magic Methods. By defining these methods in your own classes, you can make your objects behave like numbers, strings, or any other mathematical entity.


When you write a + b, Python actually tries to call a.__add__(b).

point_math.py
class Point:
def __init__(self, x, y):
self.x, self.y = x, y
def __add__(self, other):
# We return a NEW instance rather than modifying the current one
return Point(self.x + other.x, self.y + other.y)
def __repr__(self):
return f"Point({self.x}, {self.y})"
p1 = Point(1, 1)
p2 = Point(2, 2)
print(p1 + p2) # Output: Point(3, 3)

What if you try 10 + p1?

  1. Python tries 10.__add__(p1). The integer 10 doesn’t know how to add a Point, so it returns NotImplemented.
  2. Python then tries the “Reflected” (Reverse) method on the right-hand object: p1.__radd__(10).
class Point:
# ...
def __radd__(self, other):
# If 'other' is an int, add it to both x and y
return Point(self.x + other, self.y + other)
p1 = Point(1, 2)
print(10 + p1) # Point(11, 12)

In-place operators like += allow you to modify the object directly (if it is mutable) instead of creating a new one.

class Score:
def __init__(self, val):
self.val = val
def __iadd__(self, other):
self.val += other
return self # MUST return self for in-place operators

OperatorMethod
==__eq__(self, other)
!=__ne__(self, other)
<__lt__(self, other)
>__gt__(self, other)
<=__le__(self, other)
>=__ge__(self, other)
logic_comparison.py
class Player:
def __init__(self, name, level):
self.name, self.level = name, level
def __lt__(self, other):
return self.level < other.level
p1 = Player("Alice", 10)
p2 = Player("Bob", 20)
print(p1 < p2) # True

When Python sees an operator, it follows a strict protocol:

  1. Check obj1.__add__(obj2).
  2. If it returns NotImplemented, check obj2.__radd__(obj1).
  3. If both fail, raise a TypeError.

This “Double Dispatch” ensures that even if you don’t control the class on the left (like int or list), you can still make your own class work with it by implementing the reflected (r) version.