Home Python Operator Overloading
Post
Cancel

Python Operator Overloading

Built-in classes can be used with Python operators. However, the same operator responds differently to several types. For instance, the + operator will combine two lists, concatenate two strings, or perform arithmetic addition on two numbers.

Operator overloading is a Python feature that enables the same operator to have several meanings depending on the context.

So what happens when we use them with objects of a user-defined class? Let us consider the following class, which tries to simulate a point in 2-D coordinate system.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
    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)

    Output: 
    Traceback (most recent call last):
    File "<string>", line 9, in <module>
        print(p1+p2)
    TypeError: unsupported operand type(s) for +: 'Point' and 'Point'

Here, we can see that a TypeError was thrown because Python was unable to combine two Point objects. However, using operator overloading in Python, we can complete this work.

Python Special Functions

Class functions that begin with double underscore __ are called special functions in Python. These functions are not the typical functions that we define for a class. The init() function we defined above is one of them. It gets called every time we create a new object of that class.

There are numerous other special functions in Python.

Using special functions, we can make our class compatible with built-in functions.

1
2
3
4
5
    p1 = Point(2,3)
    print(p1)

    Output:
    <__main__.Point object at 0x00000000031F8CC0>

Let’s say that instead of printing what we got, we want the print() function to print the coordinates of the Point object. In our class, we may define a str() method that regulates how the item is printed. Let’s examine how we can accomplish this:

1
2
3
4
5
6
7
    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)

Trying the print() function again.

1
2
3
4
5
6
7
8
9
10
11
12
13
    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(2, 3)
    print(p1)

    Output: (2,3)

Turns out, that this same method is invoked when we use the built-in function str() or format().

1
2
3
4
5
    str(p1)
    Output: '(2,3)'

    format(p1)
    Output: '(2,3)'

So, when we use str(p1) or format(p1), Python internally calls the p1.str() method. Hence the name, special functions.

Overloading the + Operator

We must include the add() function in the class in order to overload the + operator.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
    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)

    def __add__(self, other):
        x = self.x + other.x
        y = self.y + other.y
        return Point(x, y)


    p1 = Point(1,2)
    p2 = Point(2,3)

    print(p1+p2)

    Output: (3,5)

In the above program, what exactly happens is that when we use p1+p2, Python class p1.__add__(p2) which in turn is Point.__add__(p1,p2). After this, the addition operation is carried out the way we specified.

Similary, we can overload other operators as well. The special function that we need to implement is tabulated below.

OperatorExpressionInternally
Additionp1 + p2p1.__add__(p2)
Subtractionp1 - p2p1.__sub__(p2)
Multiplicationp1 * p2p1.__mul__(p2)
Powerp1 ** p2p1.__pow__(p2)
Divisionp1 / p2p1.__truediv__(p2)
Floor Divisionp1 // p2p1.__floordiv__(p2)
Remainder(modulo)p1 %p 2p1.__mod__(p2)
Bitwise Left Shiftp1 << p2p1.__lshift__(p2)
Bitwise Right Shiftp1 >> p2p1.__rshift__(p2)
Bitwise ANDp1 & p2p1.__and__(p2)
Bitwise ORp1 | p2p1.__or__(p2)
Bitwise XORp1 ^ p2p1.__xor__(p2)
Bitwise NOT~p1p1.__invert__(p2)

Overloading Comparison Operators

Operator overloading is not restricted to arithmetic operators in Python. Additionally, we can overload comparison operators.

Suppose, we wanted to implement the < symbol to the Point class. To achieve this, let’s compare the distances between these places and the origin and then output the result.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
    # overloading the less than operator
    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)

        def __lt__(self, other):
            self_mag = (self.x ** 2) + (self.y ** 2)
            other_mag = (other.x ** 2) + (other.y ** 2)
            return self_mag < other_mag

    p1 = Point(1,1)
    p2 = Point(-2,-3)
    p3 = Point(1,-1)

    # use less than
    print(p1<p2)
    print(p2<p3)
    print(p1<p3)

    Output: 
    True
    False
    False

Similarly, the special functions that we need to implement, to overload other comparison operators are tabulated below.

OperatorExpressionInternally
Less thanp1 < p2p1.lt(p2)
Less than or equal top1 <= p2p1.le(p2)
Equal top1 == p2p1.eq(p2)
Not Equal top1 != p2p1.ne(p2)
Greater thanp1 > p2p1.gt(p2)
Greater than or equal top1 >= p2p1.ge(p2)
This post is licensed under CC BY 4.0 by the author.