Python Operator Overloading and Python Magic Methods

Free Python courses with 57 real-time projects - Learn Python

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.

So, let’s start the Python Magic Methods Tutorial.

Python Operator Overloading

Python Operator Overloading and Python Magic Methods

What is Python Operators Overloading?

In Python, an operator lets us perform an operation/procedure on one or more operands(values). Let’s take an example.

>>> 42+1

Output

43

Here, we performed the 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.

>>> '42'+'1'

Output

‘421’
>>> 'hello'+' '+'world'

Output

‘hello world’

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

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

Output

[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):        
                self.whole=whole         
                self.fraction=fraction      
      def shownumber(self):       
                print(f"I am {self.whole}.{self.fraction}")
>>> obj1=myfloat(3,7)
>>> obj1.shownumber()

Output

I am 3.7
>>> obj2=myfloat(3,3)
>>> obj2.shownumber()

Output

I am 3.3

Now, let’s try adding two objects.

>>> obj1+obj2

Output

Traceback (most recent call last):

File “<pyshell#24>”, line 1, in <module> obj1+obj2TypeError: 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.

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-

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

2. 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 |=

3. 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()

4. Python Comparison Operators

__lt__ for <

__le__ for <=

__eq__ for ==

__ne__ for !=

__ge__ for >=

__gt__ for >

Others include __radd__ for reverse add.

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

Output

3
>>> 2+a

Output

3

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

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):  
           self.whole=whole            
           self.fraction=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()

Output

I am 3.7
>>> obj2=myfloat(3,3)
>>> obj2.shownumber()

Output

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

Output

I am 7.0

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

>>> result

Output

<__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).

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):                  
               self.x=x                
        def __pow__(self,other):                               
               return self.x**other.x             
>>> a=itspower(2)
>>> b=itspower(10)
>>> a**b

Output

1024

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):
         self.name=name
         self.age=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

Output

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

Output

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

Output

10

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

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

Output

’11’
>>> 1.__add__(1)

Output

SyntaxError: invalid syntax
>>> [1,2,3].__add__([4,5,6])

Output

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

So, this was all about Python Operator overloading and Python Magic Method Tutorial. Hope you like it.

Interview Questions on Python Operator Overloading and Python Magic Methods

  1. How do you overload an operator in Python?
  2. What are the magical methods in Python?
  3. Which operator is overloaded by invert in Python?
  4. How do you call a magic method in Python?
  5. Which operators cannot be overloaded?

Conclusion

Hence, we studied 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.

Your 15 seconds will encourage us to work even harder
Please share your happy experience on Google

follow dataflair on YouTube

6 Responses

  1. srinivas says:

    Hi,i oveloaded negation operator like this.
    def __neg__(self):
    return -self.x

    -ob1
    print(ob1)
    but not getting it could you help me to resolve this.

    • DataFlair Team says:

      Hello Srinivas,

      Let this be your class:
      class A:
      def __init__(self,x):
      self.x=x
      def __neg__(self):
      return -self.x
      Now, create an object of this class:
      ob1=A(7)
      And now, perform negation on it:
      print(-ob1)
      And you’ll have -7. It negated the value of x- we can rephrase this to say it negated the object ob1.
      Hope, this will solve your query. Keep visiting DataFlair

  2. lakshmi says:

    why 1.__add__(1) is getting error?

    • DataFlair Team says:

      For understanding this, you will have to go at a little depth.
      The python tokenizer tries to build the longest possible token. so the line is split into tokens as
      “1.”, “__add__”, “(“,”1″ ,”)”
      It parses the first token as “1.” which is a float value followed by an identifier. So, it throws syntax error.

      Note: (1).__add__(1) will return 2

  3. Abi says:

    Why class names are in lowercase? Earlier lessons mentioned that CAPS should be used for classes and exceptions?

  4. DataFlair says:

    Yes classes and exception names should start with Capital letter but in this article we are studying operator overloading which is a different than normal classes, hence the class names are in lowercase.

Leave a Reply

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