# 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:**

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:**

>>>

### 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:**

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**.