**1. What is operator overloading?**

Python provides many types of operators to manipulate the data types supported by Python. An operator performs different actions on each different data type. **For example**, using the **operator +** on two numbers is addition. Using the **operator +** on two strings is the concatenation of two strings.

Python allows you to use a single operator for various operations on different data types, called **operator overloading**. This useful feature can simplify your code and make it more flexible.

A class can also be considered as a data type. When we use operators on classes, what will happen?

```
class Point():
def __init__(self, x=0, y=0):
self.x = x
self.y = y
p1 = Point(1, 2)
p2 = Point(2, 3)
print(p1+p2)
```

**Result**

```
Traceback (most recent call last):
File "c:\python-examples\main.py", line 8, in <module>
print(p1+p2)
TypeError: unsupported operand type(s) for +: 'Point' and 'Point'
```

In the example above, we create a class **Point** with objects **p1** and **p2**. Then we use the **operator +** on these two objects and the result is an error. This error is because we have not defined the **operator +** for the **Point** class to execute the **operator +** on objects of the **Point** class.

To learn how to overload operators, which means defining operators to work on new data types, we must first understand **special functions** in Python.

**2. Special functions in Python**

Functions in Python classes that start with an **underscore __** are called special functions. One specific function we have learned about is the constructor function, ** __init__()**.

In addition, a class in Python has many other special functions. These special functions can help us redefine some methods and predefined operators in Python. For example, we use Python’s ** print()** function to print an object of the

**Point**class as follows:

```
class Point():
def __init__(self, x=0, y=0):
self.x = x
self.y = y
p1 = Point(1, 2)
print(p1)
```

**Result**

`<__main__.Point object at 0x000002A6793D7D60>`

We can redefine the special function ** __str__()** to change the way the

**function prints an object of the**

`print()`

**Point**class.

```
class Point():
def __init__(self, x=0, y=0):
self.x = x
self.y = y
def __str__(self):
return "({0}, {1})".format(self.x, self.y)
p1 = Point(1, 2)
print(p1)
```

**Result**

`(1, 2)`

Operators in Python will change and add functionality when these special functions are redefined. Therefore, we can overload operators.

**3. Overloading arithmetic operators**

The **+ operator** is one of the most commonly used arithmetic operators. We will try to **overload the + operator** in Python. To do this, we need to redefine the special function ** __add__()** in the class of objects that we want the

**+ operator**to be able to execute.

```
class Point():
def __init__(self, x=0, y=0):
self.x = x
self.y = y
# define special function __str__()
def __str__(self):
return "({0},{1})".format(self.x, self.y)
# define special function __add__
def __add__(self, apoint):
x = self.x + apoint.x
y = self.y + apoint.y
return Point(x, y)
# create objects of Point
p1 = Point(1, 2)
p2 = Point(2, 3)
p3 = p1 + p2
print(p3)
```

**Result**

`(3,5)`

What happens when we perform the **+ operator** with **p1** and **p2** through the command ** p3 = p1 + p2**? Then, Python will call the function

**which will return a**

`p1.__add__(p2)`

**Point(x, y)**object. This object will be assigned to

**p3**and printed by the

**function which is redefined with the special function**

`print()`

**.**

`__str__()`

Similarly, we can also overload other arithmetic operators with special functions.

Operator | Expression | Special Function |
---|---|---|

+ | p1 + p2 | p1.__add__(p2) |

– | p1 – p2 | p1.__sub__(p2) |

* | p1 * p2 | p1.__mul__(p2) |

** | p1 ** p2 | p1.__pow__(p2) |

/ | p1 / p2 | p1.__truediv__(p2) |

// | p1 // p2 | p1.__floordiv__(p2) |

% | p1 % p2 | p1.__mod__(p2) |

You can review how to use **arithmetic operators** in the article **Types of Operators in Python**.

**4. Overloading comparison operators**

The **< operator** is one of the most commonly used comparison operators. We will try to **overload the < operator** in Python. To do this, we need to redefine the special function ** __lt__()** in the class of objects that we want the

**< operator**to be able to execute.

```
class Point():
def __init__(self, x=0, y=0):
self.x = x
self.y = y
# define special function __str__()
def __str__(self):
return "({0},{1})".format(self.x, self.y)
# define special function __lt__
def __lt__(self, apoint):
self_mag = (self.x ** 2) + (self.y ** 2)
apoint_mag = (apoint.x ** 2) + (apoint.y ** 2)
return self_mag < apoint_mag
# create objects of Point
p1 = Point(1, 2)
p2 = Point(2, 3)
p3 = Point(-1, 1)
print("p1<p2?", p1<p2)
print("p1<p3?", p1<p3)
print("p2<p3?", p2<p3)
```

**Result**

```
p1<p2? True
p1<p3? False
p2<p3? False
```

In the example above, to compare which point is smaller than the other, we compare the distance between two points to the origin **O(0, 0)**.

Similarly, we can also overload other comparison operators with special functions.

Operator | Expression | Special Function |
---|---|---|

< | p1 < p2 | p1.__lt__(p2) |

<= | p1 <= p2 | p1.__le__(p2) |

== | p1 == p2 | p1.__eq__(p2) |

!= | p1 != p2 | p1.__ne__(p2) |

> | p1 > p2 | p1.__gt__(p2) |

>= | p1 >= p2 | p1.__ge__(p2) |

You can review how to use **comparison operators** in the article **Types of Operators in Python**.

**5. Overloading bitwise operators**

We can also overload bitwise operators with special functions.

Operator | Expression | Special Function |
---|---|---|

<< | p1 << p2 | p1.__lshift__(p2) |

>> | p1 >> p2 | p1.__rshift__(p2) |

& | p1 & p2 | p1.__and__(p2) |

| | p1 | p2 | p1.__or__(p2) |

^ | p1 ^ p2 | p1.__xor__(p2) |

~ | ~p1 | p1.__invert__() |

You can review how to use **bitwise operators** in the article **Types of Operators in Python**.

**6. Overloading logical operators**

Logic operators in Python work on **boolean** values. By default, the **boolean** value of an object is **True**. If the object is **None** or **False**, the **boolean** value of the object is **False**. Python does not support the overloading of logic operators (**and, or, not**). But Python does support a special function ** __bool__()** to change the default

**boolean**value of an

**object**.

```
class Data:
def __init__(self, i):
self.id = i
def __bool__(self):
return self.id % 2 == 0
d1 = Data(6)
d2 = Data(4)
# False
print(bool(Data(3)) and bool(Data(4)))
```

Note: Python doesnotsupport function overloading.