Operator Overloading in Python

Operator overloading is a feature in Python that allows developers to redefine the behavior of built-in operators (like +, -, *, ==, etc.) for user-defined classes.

This is achieved through special built-in methods known as magic methods or dunder methods (because they start and end with double underscores, like add or str).

Why Operator Overloading?

By default, Python's operators are defined for built-in data types. For example, the + operator adds numbers or concatenates strings, but it doesn't know how to handle custom objects.

With operator overloading, you can define what + or any other operator should do when used with objects of your own class.

Example without overloading:

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

p1 = Point(2, 3)
p2 = Point(4, 5)
print(p1 + p2)  # Error
Output:

TypeError: unsupported operand type(s) for +: 'Point' and 'Point'
To make this work, we need to overload the + operator using the add() method.

Using Magic Methods

Python provides a set of special methods that you can define inside your class to override built-in operators’ behavior.

Example:

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

    def __add__(self, other):
        if isinstance(other, Point):
            return Point(self.x + other.x, self.y + other.y)
        return NotImplemented

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

p1 = Point(2, 3)
p2 = Point(4, 5)
print(p1 + p2)
Output:

(6, 8)
Here:
add() defines how the + operator behaves.
str() defines how the object should be represented as a string when printed.

Commonly Used Magic Methods in Python

Operator Magic Method Description
+ __add__(self, other) Adds two objects
- __sub__(self, other) Subtracts one object from another
* __mul__(self, other) Multiplies two objects
/ __truediv__(self, other) Divides two objects
// __floordiv__(self, other) Performs floor division
== __eq__(self, other) Checks equality of two objects
!= __ne__(self, other) Checks inequality
> __gt__(self, other) Greater than comparison
< __lt__(self, other) Less than comparison
str(obj) __str__(self) String representation of an object
len(obj) __len__(self) Returns length of an object
in __contains__(self, item) Membership test

Example: Comparison Operators

You can also overload comparison operators like == or > to compare custom objects meaningfully.

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

    def __gt__(self, other):
        return self.marks > other.marks

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

s1 = Student("Alice", 85)
s2 = Student("Bob", 90)
s3 = Student("Carol", 85)

print(s1 > s2)  # False
print(s1 == s3)  # True
Output:

False
True
Here, the operators > and == now compare marks instead of memory addresses.

Overloading __str__() and __repr__()

str() and repr() control how objects are displayed when printed or inspected in the console.

class Book:
    def __init__(self, title, author):
        self.title = title
        self.author = author

    def __str__(self):
        return f"{self.title} by {self.author}"

    def __repr__(self):
        return f"Book('{self.title}', '{self.author}')"

b = Book("1984", "George Orwell")
print(b)
print(str(b))
print(repr(b))
Output:

1984 by George Orwell
1984 by George Orwell
Book('1984', 'George Orwell')
str() → user-friendly output (for print()). It is called by the print() function, the str() built-in function, and string formatting operations.

repr() → developer-friendly representation (used in the console or debugging). It is called by the repr() built-in function, when an object is evaluated in the interactive interpreter (REPL), and in debugging tools.

Example – Subtraction and Multiplication:

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

    def __sub__(self, other):
        return Vector(self.x - other.x, self.y - other.y)

    def __mul__(self, scalar):
        return Vector(self.x * scalar, self.y * scalar)

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

v1 = Vector(4, 5)
v2 = Vector(2, 3)
print(v1 - v2)
print(v1 * 3)
Output:

Vector(2, 2)
Vector(12, 15)

Summary

Operator overloading makes your Python classes more intuitive and expressive. By implementing magic methods, you can seamlessly integrate your custom objects with Python’s built-in operations — improving code readability and flexibility.

Concept Description
Operator Overloading Redefining built-in operators for custom objects
Magic Methods Special methods (like __add__, __str__) that define operator behavior
__add__() Defines how the + operator works
__str__() Defines the object’s printable (user-friendly) representation
__eq__(), __gt__() Enable custom equality and comparison operations
__repr__() Returns a developer-friendly representation of the object
In the next article, we'll explore Abstraction in Python — how to use abstract classes and interfaces to define cleaner and more maintainable code architectures.
Share this Article