Operator overloading allows the same operator to perform different actions depending on the objects it works with. Python supports operator overloading for both built-in data types and user-defined classes.
- Operators such as + and * behave differently for different data types (for example, numbers, strings and lists).
- User-defined classes can customize operator behavior by implementing special methods such as __add__(), __sub__(), __mul__(), __eq__(), __lt__() and __gt__().
- The behavior of built-in types cannot be changed globally, but custom classes can define their own operator implementations.
The following example shows how the same operators can produce different results depending on the data type.
print(1 + 2)
print("Geeks" + "For")
print(3 * 4)
print("Geeks" * 4)
Output
3 GeeksFor 12 GeeksGeeksGeeksGeeks
Explanation:
- + adds two integers and concatenates two strings.
- * multiplies two numbers and repeats a string multiple times.
- This ability of operators to behave differently based on the operand type is known as operator overloading.
User Defined Types
By default, Python does not know how operators should behave for objects of a custom class. To define this behavior, Python provides special methods (also called magic methods) that are automatically invoked when operators are used.
Some commonly used operator methods are:
__add__(self, other) -> +
__sub__(self, other) -> -
__eq__(self, other) -> ==
For example, when we write obj1 + obj2. Python internally executes:
obj1.__add__(obj2)
If the __add__() method is defined in the class, the + operator works according to the logic provided in that method.
Example 1: The following example overloads the + operator for objects of a custom class.
class A:
def __init__(self, value):
self.value = value
def __add__(self, other):
return self.value + other.value
ob1 = A(1)
ob2 = A(2)
print(ob1 + ob2)
print(A.__add__(ob1, ob2))
print(ob1.__add__(ob2))
Output
3 3 3
Explanation:
- ob1 + ob2 automatically calls ob1.__add__(ob2). Python passes ob1 as self and ob2 as other.
- __add__() method returns the sum of the values stored in both objects.
- The last two statements show the equivalent internal method calls.
Example 2: The following example overloads the + operator to add the real and imaginary parts of two complex numbers.
class Complex:
def __init__(self, real, imag):
self.real = real
self.imag = imag
def __add__(self, other):
return self.real + other.real, self.imag + other.imag
c1 = Complex(1, 2)
c2 = Complex(2, 3)
print(c1 + c2)
Output
(3, 5)
Explanation:
- c1 + c2 calls the __add__() method. The real parts (1 + 2) are added together.
- The imaginary parts (2 + 3) are added together. The result (3, 5) represents the sum of the two complex numbers.
Overloading Comparison Operators
Operators like >, <, and == can also be overloaded.
Example 1: This code shows how to overload > operator to compare two objects based on their stored values.
class A:
def __init__(self, a):
self.a = a
def __gt__(self, other):
return self.a > other.a
ob1 = A(2)
ob2 = A(3)
if ob1 > ob2:
print("ob1 is greater than ob2")
else:
print("ob2 is greater than ob1")
Output
ob2 is greater than ob1
Example 2: This code shows how to overload both < and == operators for custom comparisons.
class A:
def __init__(self, a):
self.a = a
def __lt__(self, other):
return "ob1 is less than ob2" if self.a < other.a else "ob2 is less than ob1"
def __eq__(self, other):
return "Both are equal" if self.a == other.a else "Not equal"
ob1 = A(2)
ob2 = A(3)
print(ob1 < ob2)
ob3 = A(4)
ob4 = A(4)
print(ob3 == ob4)
Output
ob1 is less than ob2 Both are equal
Non-Associative Operators
Not all operators can be chained. Some are non-associative. For example, = and += cannot be combined in one statement.
a = 5
b = 10
c = 15
# Invalid: mixing assignment (=) and +=
# a = b = (a < b) += (b < c)
Explanation: In Python, = (assignment) and += (augmented assignment) cannot be mixed in same expression because they are non-associative.
Overloading Boolean Operators
We can also overload Boolean operators using magic methods:
- and: __and__(self, other)
- or: __or__(self, other)
- not: __not__(self)
Example: This code shows how to overload & operator so it works like logical AND on custom objects.
class MyClass:
def __init__(self, value):
self.value = value
def __and__(self, other):
return MyClass(self.value and other.value)
a = MyClass(True)
b = MyClass(False)
c = a & b
print(c.value)
Output
False
Explanation: Here, we redefined & so it works like logical AND for custom objects.
Special Methods for Operator Overloading
Python provides specific methods for each operator.
Binary Operators
| Operator | Magic Method |
|---|---|
| + | __add__(self, other) |
| - | __sub__(self, other) |
| * | __mul__(self, other) |
| / | __truediv__(self, other) |
| // | __floordiv__(self, other) |
| % | __mod__(self, other) |
| ** | __pow__(self, other) |
Comparison Operators
| Operator | Magic Method |
|---|---|
| < | __lt__(self, other) |
| > | __gt__(self, other) |
| <= | __le__(self, other) |
| >= | __ge__(self, other) |
| == | __eq__(self, other) |
| != | __ne__(self, other) |
Assignment Operators
| Operator | Magic Method |
|---|---|
| -= | __isub__(self, other) |
| += | __iadd__(self, other) |
| *= | __imul__(self, other) |
| /= | __itruediv__(self, other) |
| //= | __ifloordiv__(self, other) |
| %= | __imod__(self, other) |
| **= | __ipow__(self, other) |
Unary Operators
| Operator | Magic Method |
|---|---|
| - | __neg__(self) |
| + | __pos__(self) |
| ~ | __invert__(self) |
Advantages
Overloading operators in custom classes provides several benefits:
- Improved readability: code looks natural (e.g., ob1 + ob2 instead of ob1.add(ob2)).
- Consistency with built-in types: custom objects behave like integers, strings, etc.
- Conciseness: less boilerplate code.
- Custom behavior: extend operators to user-defined classes (e.g., vectors, matrices).
- Enhanced functionality: enable operators to work meaningfully with new data types.