Operator Overloading means:
Giving special meaning to operators for user-defined objects
Python allows objects to define how operators behave using dunder (magic) methods.
Example:
+ - * / == < []Can work on your own classes
Operators are just:
Method calls behind the scenes
Example:
a + bActually becomes:
a.__add__(b)HUGE Insight.
✔ Makes objects intuitive
✔ Improves readability
✔ Enables mathematical models
✔ Cleaner domain logic
✔ Used heavily in libraries (NumPy, Pandas, etc.)
class Number:
def __init__(self, value):
self.value = value
n1 = Number(10)
n2 = Number(20)
print(n1 + n2) # ERROR❌ Because Python doesn't know how to add objects.
class Number:
def __init__(self, value):
self.value = value
def __add__(self, other):
return Number(self.value + other.value)n1 = Number(10)
n2 = Number(20)
result = n1 + n2
print(result.value)Output:
30
✔ Magic unlocked
n1 + n2 → n1.__add__(n2)
| Operator | Dunder Method |
|---|---|
+ |
__add__ |
- |
__sub__ |
* |
__mul__ |
/ |
__truediv__ |
// |
__floordiv__ |
% |
__mod__ |
** |
__pow__ |
== |
__eq__ |
< |
__lt__ |
> |
__gt__ |
len(obj) |
__len__ |
obj[index] |
__getitem__ |
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 __sub__(self, other):
return Vector(self.x - other.x, self.y - other.y)v1 = Vector(2, 3)
v2 = Vector(1, 1)
result = v1 + v2
print(result.x, result.y)Output:
3 4
class Student:
def __init__(self, marks):
self.marks = marks
def __gt__(self, other):
return self.marks > other.markss1 = Student(85)
s2 = Student(75)
print(s1 > s2)Output:
True
Without this:
Objects print ugly memory addresses 😱
class Person:
def __init__(self, name):
self.name = name
def __str__(self):
return f"Person: {self.name}"p = Person("Alice")
print(p)Output:
Person: Alice
Extremely important.
def __add__(self, other):
return self.value + other.value # BAD✔ Should return object, not raw value (usually).
def __add__(self, other):
return Number(self.value + other.value)Crashes if other isn't Number.
def __add__(self, other):
if isinstance(other, Number):
return Number(self.value + other.value)
return NotImplemented🔥 Best practice.
Returning:
NotImplemented✔ Allows Python fallback logic
✔ Cleaner operator chaining
Operator overloading usually:
Returns NEW objects
Should NOT mutate existing ones (unless intentional)
def __eq__(self, other):
return self.value == other.valueWorks
But hash consistency issues may arise
Operator overloading should:
✔ Improve clarity
✔ Feel natural
✔ Avoid surprising behavior
Rule:
"If it confuses → Don't overload"
✔ Mathematical libraries
✔ Domain modeling
✔ DSL creation
✔ Custom containers
✔ Data science frameworks
✔ Always return correct type
✔ Use type checks
✔ Return NotImplemented when needed
✔ Keep behavior intuitive
✔ Avoid overengineering
✔ Operators = Method calls
✔ Implement via dunder methods
✔ Enables custom object behavior
✔ Used for arithmetic & comparisons
✔ Critical for clean APIs
✔ Must be used carefully
- Overload
+for custom class - Overload
-operator - Overload comparison operator (
>) - Implement
__str__ - Implement
__len__ - Implement safe type checking
- Return NotImplemented properly
- Build Vector math example