Python Operator Overloading and Python Magic Methods

1. Python Operator Overloading

In this Python tutorial, we are going to discuss Python Operator Overloading, examples of operator overloading in python, and python magic methods with some operators: python binary operator, python comparison operator, python unary operators, and python extended assignments. In the programming world, operator overloading is also called Operator Ad-hoc Polymorphism. Indeed, it is a case of polymorphism where different operators have different implementations based on their arguments. This is either defined by the programmer, by the programming language, or both.

Python Operator Overloading

Python Operator Overloading

2. An Introduction to Python Operators and Operator Overloading

In Python, an operator lets us perform an operation/procedure on one or more operands(values). Read up on our article on Python Operators. But for now, let’s take an example.

>>> 42+1


Here, we performed addition of two numbers using the addition operator. You know that we can apply this operator to Python string too. In that case, we call it the concatenation operator. Let’s discuss Python Syntax before proceeding.

>>> '42'+'1'


>>> 'hello'+' '+'world'

‘hello world’

And then when we do this to Python list, we concatenate two lists.

>>> [1,2,3]+[4,5,6]

[1, 2, 3, 4, 5, 6]

Python does this implicitly, but what for when you want to apply this operator to your own class? Can we? Let’s give it a try.

In this python operator overloading tutorial, we take a class ‘myfloat’ to represent floating-point numbers.

>>> class myfloat:           
      def __init__(self,whole,fraction):        
           def shownumber(self):       
                          print(f"I am {self.whole}.{self.fraction}") 
>>> obj1=myfloat(3,7) 
>>> obj1.shownumber()

I am 3.7

>>> obj2=myfloat(3,3) 
>>> obj2.shownumber()

I am 3.3

Now, let’s try adding two objects.

>>> obj1+obj2
Traceback (most recent call last): 
File "<pyshell#24>", line 1, in <module> obj1+obj2

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

As you can see, this raised a TypeError. But don’t fret; we can do this, we’ll discuss in a later section.

3. Python Magic Methods

We observe that python methods have double underscores before and after their names. These are special methods and are also called ‘dunders’. These help us implement functionality that a normal method can’t represent.

By now, we have come across only one magic method- __init__(). But we can, in fact, define our own magic methods to implement operator overloading in Python. With this, we can define these operators to work on our custom classes. Some of these are-

a. Python Binary Operators

__add__ for +
__sub__ for –
__mul__ for *
__truediv__ for /
__floordiv__ for //
__mod__ for %
__pow__ for **
__and__ for &
__xor__ for ^
__or__ for |
__lshift__ for <<
__rshift__ for >>

In our article on Python Methods, we discussed these.

b. Python Extended Assignments

__iadd__ for +=
__isub__ for -=
__imul__ for *=
__idiv__ for /=
__ifloordiv__ for //=
__imod__ for %=
__ipow__ for **=
__ilshift__ for <<=
__irshift__ for >>=
__iand__ for &=
__ixor__ for ^=
__ior__ for |=

c. Python Unary Operators

__neg__ for –
__pos__ for +
__abs__ for abs()
__invert__ for ~
__complex__ for complex()
__int__ for int()
__long__ for long()
__float__ for float()
__oct__ for oct()
__hex__ for hex()

d. Python Comparison Operators

__lt__ for <
__le__ for <=
__eq__ for ==
__ne__ for !=
__ge__ for >=
__gt__ for >

Read up on what we have to say about Python Variables and Python Numbers.

Others include __radd__ for reverse add.

>>> class myclass: 
def __init__(self,age): 
def __add__(self,other): 
        return self.age+other 
def __radd__(self,other): 
        return self.age+other 
>>> a=myclass(1) 
>>> a+2


>>> 2+a


If the interpreter cannot add left to right, it will call  __radd__() instead. Here, radd is in reverse/reflected add.

4. Python Operator Overloading Example

To be able to add our Python objects obj1 and obj2 for class ‘myfloat’, we can do the following.

>>> class myfloat:                 
           def __init__(self,whole,fraction):  
          def shownumber(self):                       
                 print(f"I am {self.whole}.{self.fraction}")   
          def __add__(self,other):      
                 if (self.fraction+other.fraction)>9:                                                
return myfloat(self.whole+other.whole+1,self.fraction+other.fraction-10)                                 
return myfloat(self.whole+other.whole,self.fraction+other.fraction)

Here, we added another method __add__, that takes two parameters (‘self’ and ‘other’) for the two objects. Then, it checks if the sum of the fraction parts of both objects is greater than 9. In that case, it transfers a 10 to the ‘whole’ part as a 1. This is for the carry. It then returns an object with the sums of the whole and fraction parts of both objects. However, if the condition isn’t met, it simply returns an object with the sums.

Let’s create objects obj1 and obj2 again, and try adding them.

>>> obj1=myfloat(3,7) 
>>> obj1.shownumber()

I am 3.7

>>> obj2=myfloat(3,3) 
>>> obj2.shownumber()

I am 3.3

>>> result=obj1+obj2 
>>> print(f"I am {result.whole}.{result.fraction}")

I am 7.0

As you can see, it works absolutely fine now and lets us add two objects of class ‘myfloat’.

>>> result 
<__main__.myfloat object at 0x0572FD10>

This is the resulting object of adding obj1 and obj2.

Here, the interpreter translates obj1+obj2 to obj1.__add__(obj2).

5. More Examples of Python Operator Overloading

To really understand something, once is never enough. So, let’s take another example of Operator overloading in Python.

>>> class itspower:                 
        def __init__(self,x):                  
def __pow__(self,other):                               
       return self.x**other.x             
>>> a=itspower(2) 
>>> b=itspower(10) 
>>> a**b


In this, we take a class ‘itspower’ and two methods __init__ and __pow__.

__pow__ takes two objects and returns the ‘x’ of first raised to the power of the ‘x’ of the second. When we type a**b, the interpreter converts it implicitly to a.__pow__(b).

Now, let’s take another example to demonstrate few more such magic methods.

>>> class Person: 
      def __init__(self,name,age):  
def __gt__(self,other): 
        if self.age>other.age: 
             return True 
        return False 
def __abs__(self): 
        return abs(self.age) 
def __iadd__(self,other): 
        return self.age+other.age 

>>> Nick=Person('Nick',7) 
>>> Angela=Person('Angela',5) 
>>> Nick>Angela


>>> Kim=Person('Kim',-8) 
>>> abs(Kim)


>>> Tom=Person('Tom',7)
>>> Mikayla=Person('Mikayla',3) 
>>> Tom+=Mikayla 
>>> Tom


To leave this lesson on an engaging note, we would just like to leave this code here:

>>> '1'.__add__('1')


>>> 1.__add__(1)

SyntaxError: invalid syntax

>>> [1,2,3].__add__([4,5,6])

[1, 2, 3, 4, 5, 6]

6. Conclusion

Python Operator overloading, in-effect, is pure syntactic sugar. Through it, we override a magic method to be able to use an operator on a custom class. Tell us if anything confuses you.

See Also-

– Python Features

– Python Functions and Arguments

For reference