Operator Overloading In Python with Easy Examples

In object-oriented programming, there exists this concept called “Polymorphism”. Polymorphism means “one action, many forms”. OOP allows objects to perform a single action in different ways. One way of implementing Polymorphism is through operator overloading.

In this python tutorial, we are going to learn what is operator overloading in python with examples and also about magic methods in python.

What is Operator Overloading in Python?

Operator overloading is the process of using an operator in different ways depending on the operands. You can change the way an operator in Python works on different data-types.

A very popular and convenient example is the Addition (+) operator.

Just think how the ‘+’ operator operates on two numbers and the same operator operates on two strings. It performs “Addition” on numbers whereas it performs “Concatenation” on strings.

Operators in Python work for built-in classes, like int, str, list, etc. But you can extend their operability such that they work on objects of user-defined classes too.

Let’s try it!

class bubble:
    def __init__(self, volume):
        self.volume = volume

We have defined a class bubble which has an attribute volume. Let’s see what happens when we combine two bubbles. We should get the volume of the combined bubble.

>>> b1 = bubble(20)
>>> b2 = bubble(30)

And now let’s add b1 and b2 to merge the bubbles.

>>> b1 + b2

Output:

TypeError: unsupported operand type(s) for +: ‘bubble’ and ‘bubble’

Clearly this doesn’t work right now. That’s because we haven’t extended its operability yet and it only works on built-in classes.

So how do we make these operators work on our user-defined class bubble as well? This is where “magic methods” come into the picture.

Magic methods in Python are special methods that begin and end with a double underscore( __ ).

The __init__() is one such method.

Another magic method at our disposal is the __str__() method.

The __str__() method lets you control how an object of your class gets printed.

So if we add this method to our bubble class and print an object of the class, it should work as follows:

class bubble:
    def __init__(self, volume):
        self.volume = volume

    def __str__(self):
        return "volume is " + str(self.volume)

Now do this in the terminal:

>>> b1 = bubble(20)
>>> print(b1)

Output:

volume is 20
>>>

Overloading ‘+’ operator

Lastly, let’s add the block of code which will make the ‘+’ operator operate on objects of bubble. Turns out, we have a magic method for this too, i.e., the __add__() method.

class bubble:
    def __init__(self, volume):
        self.volume = volume

    def __str__(self):
        return "volume is " + str(self.volume)

    def __add__(self, other):
        volume = self.volume + other.volume
        return bubble(volume)

In addition to ‘self’, the __add__() method takes another argument ‘other’. The ‘self’ and ‘other’ refer to the two objects acting as operands.

We perform the addition of volumes of ‘self’ and ‘other’, and then assign this value to a new variable volume. The method then returns a new object of the class bubble with volume as its instance variable.

Let’s create a new object which is the sum of 2 bubbles:

>>> b1 = bubble(20)
>>> b2 = bubble(30)
>>> b3 = b1 + b2
>>> print(b3)

Output:

Volume is 50

Now we can perform an addition on objects of our class.

Let’s see what happens behind the scenes.

  • When we add b1 + b2, the interpreter calls b1.__add__(b2).
  • And b1.__add__(b2) is actually executed as bubble.__add__(b1, b2).
  • This will then return bubble(50).
  • So, b3 = b1 + b2 is actually equivalent to b3 = bubble(50).

In this way, we can overload other operators as well.

Note that in the case of comparison operators, the magic method will return a boolean expression as a result of the comparison and not an object.

You’ll find various python operators and their magic methods in the table below.

Magic methods in Python

OPERATOR EXPRESSION MAGIC METHOD
Addition b1 + b2 __add__()
Subtraction b1 – b2 __sub__()
Multiplication b1 * b2 __mul__()
Division b1 / b2 __truediv__()
Power b1 ** b2 __pow__()
Floor division b1 // b2 __floordiv__()
Modulo operator b1 % b2 __mod__()
Bitwise left shift b1 << b2 __lshift__()
Bitwise right shift b1 >> b2 __rshift__()
Bitwise NOT ~b1 __invert__()
Bitwise AND b1 & b2 __and__()
Bitwise OR b1 | b2 __or__()
Bitwise XOR b1 ^ b2 __xor__()
Less than b1 < b2 __lt__()
Less than equal to b1 <= b2 __le__()
Greater than b1 > b2 __gt__()
Greater than equal to b1 >= b2 __ge__()
Equal to b1 == b2 __eq__()
Not equal to b1 !=  b2 __ne__()

Wrapping up!

In this article, we learned to use a very special power that comes with polymorphism in Python. We learned what is operator overloading in Python. We also learned how we can extend the operability of an operator such that it can work on our user-defined classes.