if
...else
or switch when the same type of function needs to be called on different objects.Two main types of Polymorphism.
None
and missing object types are checked throughout using if
...else
statement or isinstance()
function for achieving the same, but similar thing can be achieved using functool
's singledispatchmethod()
function or using multipledispatch/plum packages. functool
is included in the standard library and the other two are required to be installed separately using pip
.## Example 1: Simple Method Overloading in Python
class MyClass:
def printer(self, a, b):
total = a + b
print(f"Your total is {total}")
# Notice: This method name is same as above
def printer(self, a, b, name):
total = a + b
print(f"{name} your total is {total}")
# Create a instance
my_instance = MyClass()
# here 'printer(a,b,c)' has overwritten 'printer(a,b)',
# so it won't work raising 'TypeError'
my_instance.printer(20, 30, "Bob") # Bob your total is 50
# but then this will,
my_instance.printer(20, 30) # TypeError
# Note: Comment the above line to continue further execution
## Example 2: Use a single function to handle everything
class MyClass:
def printer(self, a, b, name=None):
total = a + b
if name:
print(f"{name} your total is {total}")
else:
print(f"Your total is {total}")
# Create a instance
my_instance = MyClass()
my_instance.printer(20, 30, "Bob") # Bob your total is 50
my_instance.printer(20, 30) # Your total is 50
# Now it works
multipledispatch
.## Example: Simple Method Overloading using 'multipledispatch'
## Note: You need to first install 'multipledispatch' if you haven't already,
# using "pip install multipledispatch" command
from multipledispatch import dispatch
class MyClass:
# decorate with 'dispatch' and pass the desired data type for method
# its important because using it dispatch will figure out the signature
@dispatch(int, int)
def printer(self, a, b):
total = a + b
print(f"Your total is {total}")
# A same named function with different signature
@dispatch(int, int, str)
def printer(self, a, b, name):
total = a + b
print(f"{name} your total is {total}")
my_instance = MyClass()
# Now calling methods with parameters
my_instance.printer(20, 30, "Bob") # Bob your total is 50
my_instance.printer(20, 30) # Your total is 50
## Example: Overload the addition and subtraction operator in 'MyClass'
class MyClass:
def __init__(self, *args):
self.args = args
# This is a special method to overload '+' operator
def __add__(self, my_obj):
# Note: 'my_obj' is the other object Python passes when
# the '+' operator is called
"""Define functionality behaviour for the '+' operator inside
this method, the input parameter can be of any type as required.
Just the functionality defined below should support it."""
return sum(self.args) + sum(my_obj.args)
# Similarly this is a special method to overload '-' operator
def __sub__(self, my_obj):
"""Define functionality behaviour for '-' operator."""
return abs(sum(self.args) - sum(my_obj.args))
# Create instances
my_ins1 = MyClass(90, 20, 10, 42)
my_ins2 = MyClass(10, 70, 20, 50)
# Invoking the' __add__()' method of 'MyClass'
print(my_ins1 + my_ins2) # 312
# or call with class by passing a 'self' and 'my_obj'
print(MyClass.__add__(my_ins1, my_ins2)) # 312
# Invoking the '__sub__()' method of 'MyClass'
print(my_ins1 - my_ins2) # 12
## Example: Create two classes with same named methods and
# a interface function to call these methods
class ListHandler:
my_list = [70, 30, 80, 20]
def return_addition(self):
"""Returns addition of list elements."""
return sum(self.my_list)
class DictHandler:
my_dict = {"key1": 70, "key2": 90, "key3": 10, "key4": 60}
def return_addition(self):
"""Returns addition of dict elements."""
return sum(self.my_dict.values())
# A common interface to handle same operation
def perform_addition(obj):
output = obj.return_addition()
print(output)
# Create instances
handler_l = ListHandler()
handler_d = DictHandler()
# we pass our 'handler_l' & 'handler_d' objects to 'perform_addition()'
# function and hope it works
perform_addition(handler_l) # 200
perform_addition(handler_d) # 230
# and it does, this is duck typing
## Example: Create a class and override two function's behaviour
class MyClass:
def __init__(self, *args):
self.args = args
# This special method represents the 'len()' built-in function
def __len__(self):
"""Defining behaviour here enables function overriding."""
return len(self.args)
# def __str__(self):
# """Similarly this is for print functionality for this object."""
# return " ".join((str(a) for a in self.args))
# Create a instance
my_ins = MyClass(10, 20, 30, 40, 50)
# now as the '__len__()' is implemented this will return the output
print(len(my_ins)) # 5
# this will return address of this object by default
print(my_ins) # <__main__.MyClass object at 0x000001D86547EF10>
# uncomment line number 11,12 & 13, then re-run this program
# to see change in its functionality