Operator Overloading in Python

This post deals with the concept of operator overloading, beginning with an introduction to the concept and concluding with a complete example program to demonstrate operator overloading in Python

Prerequisites: Basic idea of Classes and Objects in Python

NOTE: All programs in this post are written in Python 2.7.x

What is Polymorphism?

Polymorphism is one of the prime aspects of an object-oriented programming language like Python. The English dictionary defines polymorphism as

the condition of occurring in several different forms.

In a programming language, it refers to the ability of different objects to respond to the same message in a different way, either based on a difference in the definition of the message for the particular object-type or based on a small variation in the specification of the message.




The most common forms of polymorphism in Python include operator overloading, function overloading, function overriding, etc.

This post, however, deals with operator overloading in particular.

Operator Overloading

Operator overloading refers to the ability to define an operator to work in a different manner depending upon the type of operand it is used with.

A very noticeable example of this case in Python by default is the difference in the result obtained when using the ‘+’ operator with strings and numeric data types. Consider the following program,

n1,n2 = 17,45
s1,s2 = "17","45"

print n1+n2,"- Concatenation"
print s1+s2,"- Arithmetic Addition"

The output is as follows,

Operator Overloading in Python

The variables n1 and n2 store an integer data type and when added, they are arithmetically added. Whereas the variables s1 and s2 store numeric characters as a string and when added, they are concatenated. So in this way, the ‘+’ operator can be used to both concatenate as well perform arithmetic addition depending upon the type of operands being used along with it.

Operator overloading can be implemented by a programmer for objects of user-defined classes for most operators (arithmetic, bitwise, relational, etc.). This way, whenever the particular operator is used with an object of this class, it behaves according to definition for operator overloading in the class. Note that though there is a proposal, logical operators cannot be overloaded in Python as of now.

Method Names for Operator Overloading

There are specific method names for operator overloading in Python. It is a unique name for each operator. The user can provide a custom definition for the method with that name inside a user-defined class. Below is a table, listing those functions for some of the operators

Method Names for Operator Overloading

So to overload an operator, the corresponding method should be given a re-definition in the class. The library files originally define the utility of the operator. Hence we call it a redefinition. The function body should comprise the operation to be performed whenever the operator is used along with an object of this class. Once this is done, whenever the operator is used with objects of this class, the re-defined function is invoked.

Program to Implement Operator Overloading in Python

Consider the following program,

class complex_num:
    def __init__(self):
        self.real = 0
        self.imag = 0
    def assign(self,rl,img):
        self.real = rl
        self.imag = img
    def __add__(self,obj):
        temp = complex_num()
        r = self.real + obj.real
        i = self.imag + obj.imag
        temp.assign(r,i)
        return temp
    def __mul__(self,obj):
        temp = complex_num()
        r = self.real*obj.real - self.imag*obj.imag
        i = self.real*obj.imag + self.imag*obj.real
        temp.assign(r,i)
        return temp
    def __pow__(self,num):
        temp = complex_num()
        temp.assign(1,0)       #Unit Complex Number
        while num>0:
            temp *= self
            num -= 1
        return temp
    def __eq__(self,obj):
        if self.real == obj.real:
            if self.imag == obj.imag:
                return True
            else:
                return False
        else:
            return False
    def __str__(self):
        return str(self.real)+' + '+str(self.imag)+'i'

c1 = complex_num()
c1.assign(3,6)
print "Complex Number 1 is:",c1

c2 = complex_num()
c2.assign(8,2)
print "Complex Number 1 is:",c2

print "\nSum is:",c1+c2
print "Product is:",c1*c2
print "Complex Number 1 raised to power 4 is:",c1**4
print "\nComplex Number 1 is equal to Complex Number 2 -->",c1==c2,"statement"

One point to keep in mind is that the methods used in operator overloading are not called like other usual methods. Notice the main part of the program. In the line containing c1+c2, both c1 and c2 are objects of the complex_num class. Since the ‘+’ operator is used between them, the corresponding function (__add__()) defined in the class is invoked. The first operand serves as the calling object and is passed as the argument self. The second operand is passed as the second argument – named obj in this program. In a similar manner, other operations call their corresponding functions if redefined in the class

Now let’s understand the above program step by step

Logic Used

The class is simply used to operate on complex numbers.

Complex numbers can be added by adding their respective real and imaginary parts.

Complex numbers can be multiplied by simply applying the distributive method. (a+b)(c+d) = ac + ad + bc + bd. Further multiplying i*i gives -1. So this concept is used in the implementation of the multiplication function.

Complex numbers raised to a power is simply repeated-multiplication of the same number.

Complex numbers are said to be equal if their real and imaginary parts are correspondingly equal.

Implementation of the Logic

The assign() function simply assigns the real and imaginary parts of the complex number.

__add__(self,obj)

The __add__() function is used to overload the ‘+’ operator. The first argument – self is to pass the calling object to the function as in any other class method. The second argument – obj passes the second operand (the one after the + sign in main) to the function. That is, c1+c2, c1 becomes the object calling the overloading function (passed as self) and c2 is the second operand passed as the second argument i.e obj. The function then implements the logic mentioned above i.e adds the real and imaginary parts of c1 and c2 (passed/referred to as self and obj inside the function).

The function also creates a local object – temp, of the complex_num class and the added imaginary and real parts, are stored in the respective parts of temp. Hence temp now becomes another complex number which essentially holds the sum of c1 and c2. This is similar to adding two integers a and b and storing it in c (c = a+b). Finally, temp is returned by the function, which like any other function replaces the function call. Since the function call is in a print statement, the returned object gets printed.

__mul__(self,obj)

Here again, a temp object is created. Since the real parts of the product arise only from the multiplication of the real parts and complex parts of the operands, they are found and assigned to the real part of temp. The complex parts arise from the complex part of one operand and the real part of another, theses are found and assigned to the imaginary part of temp (Try multiplying two complex numbers on paper and notice the mentioned pattern). temp object is returned and it gets printed in main.

__pow__(self,obj)

Here, temp is assigned to a unit complex number first i.e 1+0i. Then a loop is used to multiply the complex number the required number of times. note that here the second operand passed as an argument is used as an integer and not as an object of the class. The definition can, however, be modified accordingly if a complex number is going to be passed. As in the previous methods, the temp object is returned and printed in main.

__eq__(self,obj)

In this function, the complex numbers are checked for equality. This function, unlike others, does not return an object of the same class but simply return True or False depending on the evaluation like any other relational operator. Nevertheless, the function can be defined to return any other data type like say a string.

Output

The following is the output of the program

output of operator overloading in Python

So that clarifies the use of all the overloaded operators.

Conclusion

Hence this way, the program manages to define the use of some of the operators for objects of the complex_num class. These operators otherwise would return an error when used with objects of this class, since the functionality for these operators under the given circumstances is not defined. The programmer has the liberty to make use of the built-in operators to perform the kind of operation one needs for the program.

Further note that, the second operator need not be of any specific data type. And since python does not necessitate mentioning the data type in the argument list of a method, any type of data type can be passed as an argument. However, the function must be defined in such a way so as to handle the data type received as the argument. It’s worth mentioning that the first operand must be an object of the class whose operator functions we wish to invoke because that serves as the calling object.

 

Feel free to drop off any sort of suggestions or queries below.


Leave a Reply

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