Operators overloading in Python

This post is lesson 37 of 54 in the subject Python Programming Language

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 print() function prints an object of the 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 p1.__add__(p2) which will return a Point(x, y) object. This object will be assigned to p3 and printed by the print() function which is redefined with the special function __str__().

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

OperatorExpressionSpecial Function
+p1 + p2p1.__add__(p2)
p1 – p2p1.__sub__(p2)
*p1 * p2p1.__mul__(p2)
**p1 ** p2p1.__pow__(p2)
/p1 / p2p1.__truediv__(p2)
//p1 // p2p1.__floordiv__(p2)
%p1 % p2p1.__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.

OperatorExpressionSpecial Function
<p1 < p2p1.__lt__(p2)
<=p1 <= p2p1.__le__(p2)
==p1 == p2p1.__eq__(p2)
!=p1 != p2p1.__ne__(p2)
>p1 > p2p1.__gt__(p2)
>=p1 >= p2p1.__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.

OperatorExpressionSpecial Function
<<p1 << p2p1.__lshift__(p2)
>>p1 >> p2p1.__rshift__(p2)
&p1 & p2p1.__and__(p2)
|p1 | p2p1.__or__(p2)
^p1 ^ p2p1.__xor__(p2)
~~p1p1.__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 does not support function overloading.

5/5 - (1 vote)
Previous and next lesson in subject<< Encapsulation and Polymorphism in PythonUser-Defined Exception in Python >>

Leave a Reply

Your email address will not be published. Required fields are marked *