🔧 Python OOP — Operator Overriding (Operator Overloading)

Introduction 🌟

**Operator Overriding**, also known as **Operator Overloading**, allows you to redefine how Python operators (like +, -, *, ==, etc.) behave for custom objects. By implementing special **magic methods**, you can give operators new meaning for your classes.

Note

💡 Operator Overriding is achieved using **magic methods** such as __add__, __eq__, __mul__, etc.
💡 Helps make custom objects behave like built-in types.

1. Why Operator Overriding? 🤔

  • ✔ Makes custom objects intuitive to work with
  • ✔ Supports mathematical behavior for classes
  • ✔ Enables comparisons between objects
  • ✔ Improves readability and usability

2. Basic Example — Overriding the + Operator 🧱

basic_add.py

class Book:
    def __init__(self, pages):
        self.pages = pages

    def __add__(self, other):
        return self.pages + other.pages

b1 = Book(120)
b2 = Book(130)

print(b1 + b2)  # Output: 250

__add__ defines behavior for the + operator
✔ Now adding two books returns total pages

3. Overriding Other Arithmetic Operators ➕➖✖️➗

arithmetic_overload.py

class Number:
    def __init__(self, value):
        self.value = value

    def __add__(self, other):
        return self.value + other.value

    def __sub__(self, other):
        return self.value - other.value

    def __mul__(self, other):
        return self.value * other.value

    def __truediv__(self, other):
        return self.value / other.value

n1 = Number(10)
n2 = Number(5)

print(n1 + n2)
print(n1 - n2)
print(n1 * n2)
print(n1 / n2)

✔ Enables full arithmetic support for custom objects

4. Overriding Comparison Operators (==, >, <) 🔍

comparison_overload.py

class Student:
    def __init__(self, marks):
        self.marks = marks

    def __eq__(self, other):
        return self.marks == other.marks

    def __lt__(self, other):
        return self.marks < other.marks

s1 = Student(85)
s2 = Student(90)

print(s1 == s2)
print(s1 < s2)

✔ Operators behave based on internal object data

5. Overriding the str() & repr() Output 📝

string_overload.py

class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __str__(self):
        return f"Point({self.x}, {self.y})"

    def __repr__(self):
        return f"Point(x={self.x}, y={self.y})"

p = Point(3, 4)
print(str(p))
print(repr(p))

✔ Controls how objects appear when printed

6. Real-World Example — Vector Addition 🧭

vector_example.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})"

v1 = Vector(2, 3)
v2 = Vector(1, 4)

print(v1 + v2)

✔ Allows intuitive mathematical operations on objects

7. Overriding the len() Function 📏

len_overload.py

class Team:
    def __init__(self, members):
        self.members = members

    def __len__(self):
        return len(self.members)

t = Team(["A", "B", "C"])
print(len(t))

✔ Makes custom objects compatible with built-in functions

8. Overriding Indexing & Iteration Operators 🔄

getitem_overload.py

class MyList:
    def __init__(self, data):
        self.data = data

    def __getitem__(self, index):
        return self.data[index]

l = MyList([10, 20, 30])
print(l[1])

✔ Enables list-like behavior

9. Most Common Magic Methods for Operator Overriding 🔧

OperatorMagic Method
+__add__(self, other)
-__sub__(self, other)
*__mul__(self, other)
/__truediv__(self, other)
%__mod__(self, other)
==__eq__(self, other)
>__gt__(self, other)
<__lt__(self, other)
[]__getitem__(self, index)
len()__len__(self)

10. Best Practices 💡

  • ✔ Ensure overloaded operators behave logically
  • ✔ Return new objects instead of modifying existing ones
  • ✔ Keep behavior consistent with built-in Python types
  • ✔ Validate inputs before performing operations
  • ✔ Use operator overloading sparingly — only when it improves clarity

11. Full Example — Money Class 💰

money_example.py

class Money:
    def __init__(self, amount):
        self.amount = amount

    def __add__(self, other):
        return Money(self.amount + other.amount)

    def __str__(self):
        return f"₹{self.amount}"

m1 = Money(500)
m2 = Money(700)

print(m1 + m2)

✔ Adds two Money objects and returns a new Money instance

Conclusion 🎉

>>“Operator Overriding gives life to your objects — making them behave like native Python types with expressive power.” ✨

You now fully understand Operator Overriding in Python! Want the next topic? Try Magic Methods, Method Overloading, Iterable Protocol, or Custom Classes. Just tell me! 😊