Operator Overloading in Python
Learn via video course

Overview
In object-oriented programming, there is a concept called Polymorphism that allows objects to perform a single action in different ways. Operator overloading is one of the ways to implement polymorphism.
Let’s take a real-life example of operator overloading. We have “W”, “S”, “A”, “D” keys on the keyboard. While playing a game, these keys control the movement of the player, but for writing a message, these keys print the respective character. Thus, a single key behaves differently for different purposes.
Now in the context of programming, let’s take an example of the “+” operator to understand operator overloading. The “+” operator is used to add two integers, join two strings, and merge two lists. It is achievable because the “+” operator is overloaded by int, str, and list built-in classes.
Scope of Article
- In this article, we’ll talk about operator overloading and its significance.
- We’ll learn about the special functions in Python and how Python provides the ability to overload operators using these special functions.
- We’ll then see some examples of overloading Python operators.
- Moving further, we’ll have a look at the complete list of magic methods provided by Python to overload the functionality of different kinds of operators.
Introduction
Python provides built-in operators like +, =, *, >, <, etc. These operators are used to perform a specific operation on given operand/s of predefined data types. The operator overloading concept refers to the ability of an operator to behave in different ways depending on the operands that the operator acts on.
In this article, we’ll learn about how to overload operators with examples using magic methods in Python.
What is Operator Overloading in Python?
Python operators work for predefined data types like int, str, list, etc, but we can change the way an operator works depending on the types of operands that we use. We may use any inbuilt or user-defined operand. This is the feature of operator overloading in Python that allows the same built-in operator to behave differently according to the context of the implementation of a problem.
Operator overloading in Python provides the ability to override the functionality of a built-in operator in user-defined classes.
For example, the “*” operator can be overloaded not only as a multiplier for numbers but also as a repetition operator for lists or strings.
Operator overloading is also known as Operator Ad-hoc Polymorphism.
Why Do We Need to Overload Operators in Python?
Operator overloading allows operators to have user-defined meanings on user-defined types (classes). It is used to customize the definition of Python operators for a user-defined class.
Let’s take an example of a user-defined class ComplexNumber to understand the need for operator overloading in Python:
Here we have defined our custom class ComplexNumber which is used to represent a complex number having a real value and an imaginary value.
Now suppose we want to add two complex numbers. We know that complex numbers are added by adding the real parts and imaginary parts separately which results in a new complex number. So we can simply use the “+” operator with two objects of ComplexNumber:
If we run the above code, it’ll throw the following TypeError at the print() statement:
This error means that we cannot perform addition operation on operands of type ComplexNumber because the “+” operator is by default overloaded for primitive data types and not for user-defined classes. The interpreter doesn’t know what to do when the “+” operator is invoked for operands of type ComplexNumber. It is necessary to overload the “+” operator in the user-defined class.
We’ll see how to overload the “+” operator in class ComplexNumber in the next section.
How to Overload Operators in Python?
Let’s see an example of the different behavior of the “+” operator in Python. We will directly execute some commands to see the output.
Let’s see the output of using the “+” operator on two integers:
As we can see, the “+” operator does the addition of two numbers. Now let’s try to use the “+” operator on two strings:
Here, we can see that the output is the concatenation of two strings. Now we will see what happens when we use the “+” operator on two lists:
We can see that the output is the merged list of the two lists. So we can see that Python operators work differently for different types of operands. So this is an example of overloading the “+” operator because the same operator performs different actions on int, str, list, etc types.
As we have already seen in the previous section, they do not work for custom types. To understand how we can overload operators for custom types, we first need to get a hang of Python special functions. Let’s see what they are and how they help in operator overloading in Python.
Python Special Functions
In Python we have certain functions which are used to perform some special tasks, these functions are known as special functions. These functions begin and end with a “__” (double underscore).
The init() function we defined for our ComplexNumber class is an example of a special function that gets called every time we create a new object or instance of the ComplexNumber class.
We can make our classes compatible with built-in functions in Python by using the special functions. There are various other special functions in Python, for example, the __str__() special function is used to print a custom class through the print() method.
Now that we know about the special functions we can move ahead to see how we use special functions for operator overloading in Python.
In Python, we have certain special functions also known as magic methods. These special functions are invoked automatically by Python in certain special situations, or we may also invoke them ourselves.
These special functions can be used to overload the built-in operators for our custom classes. For example, the __add__() special function is used to overload the “+” operator for our custom classes.
We’ll look in detail along with examples of how to use these magic methods or special functions for operator overloading in Python.
Examples of Operator Overloading in Python
In this section, we’ll look at examples of how to overload different types of operators for various user-defined data types. We’ll be using the ComplexNumber class we created in the previous section for demonstration.
1. Overloading “<” Operator
Let’s see how we can overload the less than (<) operator. By doing this, we can easily compare our ComplexNumber objects by using the “<” operator.
To do this, we need to implement the __lt__() special function in the complex number class:
The output of the above code is:
In the output received, we can see that since 1 + 2i is less than 3 + 4i as real part 1 is less than real part 3, we get the output that 1 + 2i is less than 3 + 4i.
As you can see, we have added the implementation for the __lt__() method. It takes one parameter i.e. self like any other class method and another complex number object as its second parameter. Then it compares the real part of the self-object with the real part of the other passed object and if they are equal we compare the imaginary parts to get the result.
We have also added the implementation of __str__() special function to provide the object in a printable format that can be used by the print() function.
What happens behind the scenes is that when we do complex_number1 < complex_number2, Python in turn calls the complexnumber1.__lt__(complexnumber2) which actually is ComplexNumber.__lt__( complexnumber1,complexnumber2). The less than comparison then happens the way we have specified in the special function.
2. Overloading “+” Operator
Now let’s overload the “+” operator in our ComplexNumber class in the below code:
In the above code, we have added the implementation of the __add__() special function to overload the “+” operator. Just like with the “<” operator it takes two arguments, one is the self object and the other is the ComplexNumber object to perform addition with. In this, we just add the real parts and imaginary parts of the complex numbers separately and return a new complex number made from the addition of these parts.
The output of the above code is:
In the above output, we can see our addition is the result of the addition of real numbers (1 and 3) and imaginary numbers (2i and 4i).
3. Overloading Comparison Operators
We have seen how we can overload the less than(<) operator. Let’s see an example where we will overload all the comparison operators(like >, !, <=, >=, etc):
The output of the above code is:
In the above code, we have overloaded the six comparison or relational operators by comparing the real and imaginary parts of the two complex numbers. You can try this code with various inputs to see the working of all these operators.
4. Overloading Equality Operator
Now, let’s overload the equality(==) operator to check if two ComplexNumber objects are equal or not. Below is the code to overload the “==” operator:
For overloading the “==” operator, we need to overload the __eq__() special function. In the implementation of the__eq__() method we check if the real parts of two complex numbers are equal or not, if not we check the equality of the imaginary parts and accordingly return the response.
The output of the above code is:
In the output, we can see that since the real parts of the complex number (1 and 3) are not equal, we get the output that complex numbers are not equal.
Magic Methods for Operator Overloading in Python
We have seen how to overload Python operators using special functions or magic methods. We’ll now have a look at the different predefined magic methods given by Python for various operators.
We are taking two demo objects C1 and C2 of ComplexNumber type for explanation purposes:
1) Binary Operators
Operator | Operator Name | Magic Method | Expression | Internal Calls |
---|---|---|---|---|
>> | Bitwise Right Shift | __rshift__(self, other) | C1>>C2 | C1.__rshift__(C2) |
<< | Bitwise Left Shift | __lshift__(self, other) | C1<<C2 | C1.__lshift__(C2) |
& | Bitwise AND | __and__(self, other) | C1&C2 | C1.__and__(C2) |
I | Bitwise OR | __or__(self, other) | C1IC2 | C1.__or__(C2) |
^ | Bitwise XOR | __xor__(self, other) | C1^C2 | C1.__xor__(C2) |
2) Comparison Operators
Operator | Operator Name | Magic Method | Expression | Internal Calls |
---|---|---|---|---|
< | Less than | __lt__(SELF, OTHER) | C1<C2 | C1.__lt__(C2) |
> | Greater than | __gt__(SELF, OTHER) | C1>C2 | C1.__gt__(C2) |
<= | Less than or equal to | __le__(SELF, OTHER) | C1<=C2 | C1.__le__(C2) |
>= | Greater than or equal to | __ge__(SELF, OTHER) | C1>=C2 | C1.__ge__(C2) |
== | Equal to | __eq__(SELF, OTHER) | C1==C2 | C1.__eq__(C2) |
!= | Not equal to | __ne__(SELF, OTHER) | C1!=C2 | C1.__ne__(C2) |
3) Assignment Operators
Operator | Operator Name | Magic Method | Expression | Internal Calls |
---|---|---|---|---|
= | Subtract AND | __isub__(self, other) | C1-=C2 | C1.__isub__(C2) |
+= | Add AND | __iadd__(self, other) | C1+=C2 | C1.__iadd__(C2) |
*= | Multiply AND | __imul__(self, other) | C1*=C2 | C1.__imul__(C2) |
/= | Divide AND | __idiv__(self, other) | C1/=C2 | C1.__idiv__(C2) |
//= | Divide(floor) AND | __ifloordiv__(self, other) | C1//=C2 | C1.__ifloordiv__(C2) |
%= | Modulus AND | __imod__(self, other) | C1%=C2 | C1.__imod__(C2) |
**= | Exponent AND | __ipow__(self, other) | C1**=C2 | C1.__ipow__(C2) |
>>= | Performs Bitwise right shift and assign value to left operand | __irshift__(self, other) | C1>>=C2 | C1.__irshift__(C2) |
<<= | Performs Bitwise left shift and assign value to left operand | __ilshift__(self, other) | C1<<=C2 | C1.__ilshift__(C2) |
&= | Performs Bitwise AND and assign value to left operand | __iand__(self, other) | C1&=C2 | C1.__iand__(C2) |
I= | Performs Bitwise OR and assign value to left operand | __ior__(self, other | C1I=C2 | C1.__ior__(C2) |
^= | Performs Bitwise XOR and assign value to left operand | __ixor__(self, other) | C1^=C2 | C1.__ixor__(C2) |
4) Unary Operators
Operator | Operator Name | Magic Method | Expression | Internal Calls |
---|---|---|---|---|
– | Unary Plus | __neg__(self) | -C1 | C1.__neg__() |
+ | Unary Minus | __pos__(self) | +C1 | C1.__pos__() |
~ | Unary Invert | __invert__(self) | ~C1 | C1.__invert__() |
5) Mathematical Operators
Operator | Operator Name | Magic Method | Expression | Internal Calls |
---|---|---|---|---|
+ | Addition | __add__(self, other) | C1-C2 | C1.__sub__(C2) |
– | Subtraction | __sub__(self, other) | C1+C2 | C1.__add__(C2) |
* | Multiplication | __mul__(self, other) | C1*C2 | C1.__mul__(C2) |
/ | Division (float) | __truediv__(self, other) | C1/C2 | C1.__truediv__(C2) |
// | Division (floor) | __floordiv__(self, other) | C1//C2 | C1.__floordiv__(C2) |
% | Modulus | __mod__(self, other) | C1%C2 | C1.__mod__(C2) |
** | Exponential | __pow__(self, other) | C1**C2 | C1.__pow__(C2) |
Advantages of Operator Overloading
- Code readability: Operator overloading allows developers to use familiar operators to perform operations on user-defined objects. This makes code more intuitive and easier to read, especially when working with complex data types.
- Simplicity: Overloading operators allows developers to create simpler, more elegant code that is easier to understand and maintain. It can reduce the amount of code needed to perform certain operations, making programs more concise and efficient.
- Flexibility: By overloading operators, developers can define custom behavior for built-in operators to work with their own classes. This provides a great deal of flexibility in how classes are used, and can be especially useful in libraries and APIs.
- Consistency: Operator overloading can help ensure consistency in how objects behave, making it easier to reason about code and debug errors.
- Ease of use: Overloading operators makes it easier for developers to perform common operations, since they can use familiar operators to perform them. This can improve the developer experience and speed up the development process.
- Customization: Overloading operators allows developers to create custom operations for their classes, making them more powerful and flexible. This can be especially useful for numerical and scientific computing, where custom operations are often needed.
- Integration with built-in functions: Python's built-in functions and modules often rely on operator overloading, making it an important tool for integrating user-defined objects with the rest of the language. This can improve the performance and functionality of Python programs.
Conclusion
- Operator overloading is one of the ways to implement Polymorphism.
- Operator overloading provides the ability to override the functionality of a built-in operator in user-defined classes.
- Operator overloading in Python is done using special functions or magic methods.
- Python provides a predefined list of special functions or magic methods for different operators.