Operator Overloading in Python
Overview
In object-oriented programming, polymorphism facilitates varied actions through concepts like operator overloading. Operators like "+" in Python can execute different actions based on context—such as adding integers, concatenating strings, or merging lists.
Example of Python Operator Overloading
Below is an example of Operator Overloading in Python. In this example, we'll create a class representing a complex number and then overload the + and * operators.
The output of the above code is:
How to Overload Operators in Python?
Let’s see an example of the different behaviour of the “+” operator in Operator Overloading 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 concatenates 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. We can see that Python operators work differently for different types of operands.
Overloading binary “+” operator in Python
Now let’s overload the “+” operator in ComplexNumber class in the below code:
In the above code, we have implemented the __add__() particular function to overload the “+” operator.
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).
How Does the Operator Overloading Work?
The basic idea behind Operator Overloading in Python is to define special methods in your class that correspond to the operators you want to overload. Let's understand it with these examples:
- Multiplying an Object with a Scalar
The output of the above code is:
- String Representation
The output of the above code is:
Overloading comparison operators in Python
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.
Overloading equality and less than operators
1. Overloading “<” Operator
Let’s see how we can overload the less than (<) 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 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__() particular 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 as we specified in the special function.
2. 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 implementing the__eq__() method, we check if the real parts of two complex numbers are equal; 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 numbers (1 and 3) are not equal, we get the result that complex numbers are not equal.
Python magic methods or special functions for operator overloading
We’ll now look at the different predefined magic methods in Operator Overloading 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) 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) |
3) 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__() |
Operator overloading on Boolean values
Within Python, it's possible to customize the behaviour of the Boolean operators __and__, __or__, and __not__ by defining the particular methods in your class.
Let's take an example of overloading or operator:
When working with custom classes, you might want to define a specific behaviour for the __or__ operator based on the characteristics of your objects. This is where operator overloading comes into play.
The output of the above code is:
Here, the result object represents the logical OR operation between obj1 and obj2. The print(result.value) statement outputs True, indicating that at least one of the boolean values is True according to the custom logic defined in the __or__ method.
Advantages
- Customized Logic: Operator Overloading in Python allows you to implement logic specific to your class using Boolean operators. This customization can lead to more intuitive and expressive code.
- Readability: By overloading Boolean operators, you can create code that reads more like natural language, making it easier to understand the intent of the operations.
- Consistency: Operator Overloading in Python helps maintain consistency in your codebase, especially when dealing with custom classes. It ensures that logical operations behave in a way that is consistent with the rest of your application.
- Abstraction: Operator Overloading in Python allows you to abstract away complex logic within your class, providing a cleaner interface for users of your class.
Conclusion
- Operator Overloading in Python is one of the ways to implement Polymorphism.
- Operator Overloading in Python 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 particular functions or magic methods for different operators.