Register Login

Overloading Functions and Operators in Python

Python's Object-Oriented programming comes with a robust feature that can make a single operator or function work differently depending on different situations. Programmers call this concept overloading, and it comes as a part of Python Polymorphism. In this article, you will learn about overloading and its various implementation in detail.

What is Overloading:

Overloading is an object-oriented programming concept that tells us the ability of a function or operator to behave differently depending on the parameter passed (in case of functions) or the operand type acting on any overloaded operator. It comes as a part of polymorphism which means multiple forms of a single element. It means that a single program element will work differently depending on the different signatures being used with it. The basic purpose of using overloading is the reusability of a function or operator. Overloading also helps in improvising the clarity and makes your program smart. There are two different ways you can use overloading in your program.

  • Function Overloading
  • Operator Overloading

Function Overloading:

Like that of other programming languages (C++, Java, C#, etc.), Python also offers to implement function overloading, where you can call the function at a different point in the program with zero, one, two, or even many parameters.
In Python, we can again sub-divide a function overloading into two types:

  • built-in functions and
  • custom or user-defined functions

Built-in Function / Pre-defined function overloading: Overloading built-in functions require determining any pre-defined Python function with additional functionalities and parameters. It makes the program overload the function within the python class as a special function. Python interpreter will use this newly structured function within the program run as the declaration for the pre-defined call.

class Employee:

def __init__(self, collection, seller):
    self.collection = list(collection)
    self.seller = seller
def __len__(self):
    return len(self.collection)
emp1 = Employee(['Insurance', 'Stock-Market', 'Loan', 'Stock-Market'], 'Karlos Ray')
print("The employee", emp1.seller, "has collected", len(emp1), "different things")

Explanation:

Here I've taken an Employee class and initialized the seller and collection parameters within __init__() function. You can notice here that instead of using the usual built-in len() function of Python, we defined the overloaded function __len__() using double underscore with the pre-defined method name where it returns the length of the collection items. Now, you can create an object (emp1) of the Employee class. Then we can use the len(emp1) to calculate the length of the collection object that was previously passed as a parameter in the __init__().

Custom or user-defined functions: User-defined or custom functions that the programmer defines and create to make code reusability and call it whenever required. Calling such functions executes the set of statements created within it. Python allows overloading such user-defined functions where a programmer will create multiple functions with a single function name. There are two ways of overloading a user-defined function.

Type 1: Function overloading using argument unpacking operator (*) & conditional statements

Example:

def addition(dtype, *argu):
    if dtype == 'int':
        res = 0
    if dtype == 'str':
        res = ''
    for x in argu:
        res = res + x
    print(res)
  
# Calling the String
addition('str', 'Hey ', 'Karlos')
# Integer
addition('int', 20, 10)

Explanation:

The addition function takes two parameters. Out of which, one is dtype for taking the data type information as argument. The *argu is argument unpacking operator which accepts any number of parameter and of any type. Inside the function, we are checking whether the dtype variable is integer or string. If it is an integer, we are doing the simple arithmetic addition, and if it is a string, we are merging two string and performing the string concatenation. This way, depending on the dtype parameter, this function is performing different operation at different situation.

Type 2: Function Overloading using multiple parameters:

Example:

def addi(x, y):
    print(x + y)

def addi(x, y, z):
    print(x + y + z)

addi(10, 20, 30) 	#function call

Explanation:

Here we are taking two different function but with the same name. You can notice that the first function has two parameters while the second function has three parameters. Depending on the number of arguments passed at the time of function call, the addi function will be chosen at run-time. Here, we have passed three arguments during function call, hence the second function will get executed.

Type3: Function Overloading using Single Dispatch Decorator:

Before implementing this, you have to first import the singledispatch Python library.

Example:

from functools import singledispatch
@singledispatch
def addi(a, b):
    raise NotImplementedError('ERROR: Data type not found')
@addi.register(int)
def _(a, b):
    print("First parameter received is of type ", type(a))
    print("Addition of Integer: ", a + b)
@addi.register(str)
def _(a, b):
    print("First parameter received is of type ", type(a))
    print("Performing String concatenation: ", a + b)
@addi.register(list)
def _(a, b):
    print("First parameter received is of type", type(a))
    print("Merge the lists: ", a + b)
if __name__ == '__main__':
    addi(10, 20)
    addi('Function', 'Overloading')
    addi([2, 4, 6, 8], [3, 5, 7, 9])

Explanation:

Simple Dispatch is a module that we have to install and import in our Python program to use function overloading. Now, we have to create a function addi() with two paraeters. Here function overloading will happen depending on the type of input parameter passed. You can notice that the single dispatch is used to register addi with different parameter-type every time to the first parameter. For overloading, it first checks the data type and based on that, it will perform the operation. If no type is mentioned for the first parameter, wit will raise an exception showing an error message. In the second case; if the addi() has a integer value, it will perform the simple integer addition; if it’s a string, it performs String concatenation; if the parameter is found a list object, it will perform the merging of lists. Now, we check the __name__ and within it calls the addi() function three times – first with integer values (10 and 20), then with string values ('Function', 'Overloading'), and lastly with 2 lists [2, 4, 6, 8], [3, 5, 7, 9].

Type 4: Function Overloading using multiple conditional statements only:

Example:

class Calc:
    def multiArea(self, p1 = None, p2 = None):
        if p1 != None and p2 != None:
            return p1 * p2
        elif p1 != None:
            return 3.14*p1**2
        else:
            return 0

# declaring object of Calc class
calc = Calc()
# function called with no argument
print("Area Not found due to ", calc.multiArea(), "argument passed")
# function called with one argument
print("Area of Circle:",calc.multiArea(10))

# function called with two argument
print("Area of Rectangle:", calc.multiArea(10, 20))

Explanation:

Here we have created the multiArea() function containing two parameters p1 and p2. Now, based on the conditional statements, it will return some calculated values back to the function. We can note that the overloading takes place based on the number of parameters with not-None value. If both the parameters are not none, that is, they hold some value, the function will return p1 * p2. If first parameter (p1) is none, the function return (3.14*p1**2). Lastly, if both are none, the function will return zero.

Operator Overloading:

Operator overloading is the concept that helps in extending meaning to the existing Python operators so that they can operate beyond their predefined meaning. By default, Python uses some operators with overloaded capabilities. For example, we use the + operator for both addition and string concatenation. Again the * operator is used as multiplication when it has two operands (as binary operator) and argument unpacking operator when used in a function parameter to take multiple arguments (as unary operator).

# adding 2 numbers
print(20 + 10)
# concatenating 2 strings
print("Hey" + "Karlos")

Operator Overloading Program

class Overloaded:
    def __init__(self, x1):
        self.x1 = x1

    def __add__(self, x2):
        return self.x1 + x2.x1

obj1 = Overloaded(40)
obj2 = Overloaded(10)
obj3 = Overloaded("Function-")
obj4 = Overloaded("Overloading")
print(obj3 + obj4)
print(obj1 + obj2)

Explanation:

Here we have used __init__() to initialize a value (x1). Also, we have created another function __add__() to add the value of x1 and x2 passed as objects for the class Overloaded. This way, we have created a overloaded operator + that will combine two objects using the plus operator depending on the type. obj3 and obj4 have string type value and hence, performing overloaded addition will add the two objects. Again, obj1 and obj2 have integer values; therefore, the overloaded + will add these two object values.

Valid Python operators, we can overload

Here are some of the operators that programmers can overload.

Operator Expression

Addition

p1 + p2

Subtraction

p1 - p2

Multiplication

p1 * p2

Power

p1 ** p2

Division

p1 / p2

Floor Division

p1 // p2

Remainder (modulo)

p1 % p2

Bitwise Left Shift

p1 << p2

Bitwise Right Shift

p1 >> p2

Bitwise AND

p1 & p2

Bitwise OR

p1 | p2

Bitwise XOR

p1 ^ p2

Bitwise NOT

~p1

Less than

a < b

Greater than

a > b

Less than and Equal

a <= b

Greater than and equal

a >= b

Not equal

!a

Minux equal

-=

Plus Equal

+=

Multiply Equal

*=

(Integer) Divide Equal

/=

(Floor) Divide equal

//=

Modulo equal

%=

Power Equal

**=

Right-shift equal

>>=

Left-shift equal

<<=

And Equal

&=

Or Equal

|=

XOR equal

^=

Conclusion:

Both function and operator overloading are powerful object-oriented tools we can use for increasing code flexibility. In this article, we have learned four different types of function overloading. Out of these, type one is less efficient but easy to use; type two is the most commonly used function overloading when we do not have the concept of class. Using the Single Dispatch Decorator makes our program heavy but, if we know how to use it - it can increase our development. Programmers prefer the fourth type when there is a slight change in the parameters. Based on that minute change, we can place distinct operations.