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.
1. Basic Arithmetic: __add__ and __sub__
Section titled “1. Basic Arithmetic: __add__ and __sub__”When you write a + b, Python actually tries to call a.__add__(b).
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)2. Reflected Operators: __radd__
Section titled “2. Reflected Operators: __radd__”What if you try 10 + p1?
- Python tries
10.__add__(p1). The integer10doesn’t know how to add aPoint, so it returnsNotImplemented. - 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)3. In-Place Operators: __iadd__
Section titled “3. In-Place Operators: __iadd__”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 operators4. Comparison Operators
Section titled “4. Comparison Operators”| Operator | Method |
|---|---|
== | __eq__(self, other) |
!= | __ne__(self, other) |
< | __lt__(self, other) |
> | __gt__(self, other) |
<= | __le__(self, other) |
>= | __ge__(self, other) |
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) # True5. Under the Hood: The Dispatch Mechanism
Section titled “5. Under the Hood: The Dispatch Mechanism”When Python sees an operator, it follows a strict protocol:
- Check
obj1.__add__(obj2). - If it returns
NotImplemented, checkobj2.__radd__(obj1). - 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.