Sitemap

Operator overloading in Python — 1

5 min readSep 21, 2024

--

Press enter or click to view image in full size
Python Object oriented programming language (Arithmetic Operators Overloading)

Operator overloading is a kind of polymorphism in object-oriented programming. It allows a single operator to have different behaviors depending on the types of object and parameters it has to operate.

Example: The plus (+) operator functions in distinct ways depending on the type of objects it is working with. When applied to numerical objects such as integers, floats, or complex numbers, the plus operator performs arithmetic addition and produce numerical output.

On the other hand, when the plus (+) operator is used with sequence objects like strings, lists, or tuples, it performs concatenation instead of addition. This means that it combines the sequences together to form a new sequence.

How object handle operator operation in Python

In Python, every operator has an associated method; whenever an operator is used in any expression, then that associated method is called internally.

Example: If an expression is called Like: `5 + 8`, then it becomes `5.__add__(8)`, and this `__add__()` method performs the addition arithmetic operation and produces the result.

This type of methods which has double underscore before and after the name, called `Dunder` or `Magical methods` in python and these magical methods can be override in class.

Type of operators and it’s magic methods in python

Get Sanjeev Tyagi’s stories in your inbox

Join Medium for free to get updates from this writer.

Operators classification based on use:

Arithmetic operators

Operators or functions that perform mathematical operations such as Addition , Subtraction, Division, Multiplication, Reminder, exponent , floor division. Example of overloading of arithmetic operators are as below:

class MyArithmeticOperations():

def __init__(self, val):
self.val = val

def __add__(self, other): # + operator overloading

if( isinstance(other, int) or isinstance(other, float)):
return self.val + other
elif( isinstance(other, MyArithmeticOperations) ):
return self.val + other.val
else:
raise TypeError(f"Addition operation do not support for {other}")

def __sub__(self, other): # - operator overloading

if( isinstance(other, int) or isinstance(other, float)):
return self.val - other
elif( isinstance(other, MyArithmeticOperations) ):
return self.val - other.val
else:
raise TypeError(f"Subtraction operation do not support for {other}")

def __mul__(self, other): # * operator overloading

if( isinstance(other, int) or isinstance(other, float)):
return self.val * other
elif( isinstance(other, MyArithmeticOperations) ):
return self.val * other.val
else:
raise TypeError(f"Multiplication operation do not support for {other}")

def __pow__(self, other): # ** operator overloading

if( isinstance(other, int) or isinstance(other, float)):
return self.val ** other
elif( isinstance(other, MyArithmeticOperations) ):
return self.val ** other.val
else:
raise TypeError(f"Exponential operation do not support for {other}")

def __truediv__(self, other): # / operator overloading

if( not other or other == 0) : raise ValueError("Denominator can not be zero for division operation!")

if( isinstance(other, int) or isinstance(other, float)):
return self.val / other
elif( isinstance(other, MyArithmeticOperations) ):
return self.val / other.val
else:
raise TypeError(f"Division operation do not support for {other}")

def __floordiv__(self, other): # // operator overloading

if( not other or other == 0) : raise ValueError("Denominator can not be zero for division operation!")

if( isinstance(other, int) or isinstance(other, float)):
return self.val // other
elif( isinstance(other, MyArithmeticOperations) ):
return self.val // other.val
else:
raise TypeError(f"Floor Division operation do not support for {other}")

def __mod__(self, other): # % operator overloading

if( not other or other == 0) : raise ValueError("Denominator can not be zero for division operation!")

if( isinstance(other, int) or isinstance(other, float)):
return self.val % other
elif( isinstance(other, MyArithmeticOperations) ):
return self.val % other.val
else:
raise TypeError(f"Remainder (modulo) operation do not support for {other}")

Lets create object for our MyArithmeticOperation class and test with mathematical operators:


obj1 = MyArithmeticOperations(100)
obj2 = MyArithmeticOperations(15)


print( obj1 + 200 ) # Addition operation with integer: output 300
print( obj1 + obj2) # Addition operation with second object: output 115

print( obj1 - 50 ) # Subtract operation with integer: output 50
print( obj1 - obj2) # Subtract operation with second object: output 85

print( obj1 * 5 ) # Multiplication operation with integer: output 500
print( obj1 * obj2) # Multiplication operation with second object: output 1500

print( obj1 / 5 ) # Division operation with integer: output 20.0
print( obj1 / obj2) # Division operation with second object: output 6.666666666666667

print( obj1 // 9 ) # Floor Division operation with integer: output 11
print( obj1 // obj2) # floor Division operation with second object: output 6

print( obj1 % 8 ) # Mod operation with integer: output 4
print( obj1 % obj2) # Mod operation with second object: output 10

Use Case

Let’s consider a practical scenario involving an employee ID string used within an organization. This ID consists of multiple segments, formatted as follows: <branch id>-<user id>-<department id>. In this system, when a new employee is added, only the <user id> segment is updated, while the other segments remain unchanged.

For instance, if we have an existing employee ID generator with the value 4521–9001–25541, and there is a requirement to generate a new employee ID, we need to increment only the `<user id>` component. All other components are fixed, meaning that only addition operations are permitted.

To implement this, we can create a class called EmployeeID that includes an addition operation specifically for updating the <user id>. This class will ensure that the integrity of the other segments remains intact while allowing for the creation of new employee IDs as needed.

Here’s how the EmployeeID class can be structured:

class EmployeeID:
def __init__(self, e_id):
(self.b_id, self.u_id, self.dept_id) = [int(i) for i in e_id.split('-')]
self.status = "Active"

def __add__(self, val):

if( isinstance(val, int)):
self.u_id += val
else:
raise TypeError(f"{val} is not an integer.")

return f"{self.b_id}-{self.u_id}-{self.dept_id}"

def __sub__(self, val):
if (isinstance(val, int)): self.status = "Deactive"

# Addition operation
emp_id = EmployeeID("4521-9001-25541")
new_emp_id = emp_id + 1
print(f"New Id: {new_emp_id}, and \nStatus: {emp_id.status}")

# Output
#-------------------
# New Id: 4521-9002-25541, and
# Status: Active

# Subtraction operation
employee_id_generator - 1
print(f"New Id: {new_emp_id}, and \nStatus: {emp_id.status}")

# Output
#-------------------
# New Id: 4521-9002-25541, and
# Status: Deactive

In this example, the EmployeeID class allows for the addition of a new employee by simply incrementing the <user id>. The other components <branch id> and <department id> remain unchanged, ensuring that the output is consistent with the specified conditions.

So operators operation can be controlled on complex data using operator overloading.

Next: Operator overloading in Python — 2 (In-Place operators)

--

--