In Python, we use the functools
module to create higher-order functions that interact with other functions. The higher-order functions either return other functions or operate on them to broaden their scope without modifying or explicitly defining them.
singledispatch
decoratorThe singledispatch
decorator is used for function overloading. The decorator turns a function into a generic function that can have different implementations depending on the function’s first argument.
We can use type annotation for the first function argument depending on which function implementation is chosen. The decorator will infer the type of the first argument automatically.
Refer to the example code below:
from functools import singledispatch@singledispatchdef func(arg1, arg2):print("default implementation of func - ", arg1, arg2)@func.registerdef func_impl_1(arg1: str, arg2):print("Implementation of func with first argument as string - ", arg1, arg2)@func.registerdef func_impl_2(arg1: int, arg2):print("Implementation of func with first argument as string - ", arg1, arg2)func(1, "hello")func("test", "hello")func(1.34, "hi")
Line 1: We import the singledispatch
from the functools
module.
Line 3–5: We define a function named func
to accept two arguments, arg1
and arg2
. The func
is decorated with a singledispatch
decorator. This is the default implementation of the function func
.
Line 7-9: We register the first implementation of func
for which arg1
shall be of type string. Here, arg1
is annotated with string
as its type.
Line 11-13: We register the second implementation of func
for which arg1
shall be of type int
. Here, arg1
is annotated with int
as its type.
Line 15-17: We invoke func
with different values for arg1
.
Line 17: We execute the default implementation of the function as there is no implementation of func
for the float
type.
register
attribute for function overloadingIf the code doesn’t use type annotations, the relevant type argument can be passed explicitly to the decorator via register
.
Refer to the example code below:
from functools import singledispatch@singledispatchdef func(arg1, arg2):print("default implementation of func - ", arg1, arg2)@func.register(str)def func_impl_1(arg1, arg2):print("Implementation of func with first argument as string - ", arg1, arg2)@func.register(int)def func_impl_2(arg1, arg2):print("Implementation of func with first argument as int - ", arg1, arg2)func(1, "hello")func("test", "hello")func(1.34, "hi")
Line 1: We’ll import the singledispatch
from the functools
module.
Line 3-5: A function named func
is defined that accepts two arguments, arg1
and arg2
. The func
is decorated with a singledispatch
decorator. This is the default implementation of the function func
.
Line 7–9: We register the first implementation of func
for which arg1
should be of type string. This is done by providing str
to the register
attribute.
Line 11-13: We register the second implementation of func
for which arg1
should be of type int
. This is done by providing int
to the register
attribute.
Lines 15-17: We invoke func
with different values for arg1
.
Line 17: We execute the default implementation of the function as there is no implementation of func
for the float
type.
We specify multiple types with the exact implementation by registering the function with different types numerous times.
Line 11-14: We register multiple types for the exact implementation of func
.
Lines 15-17: We invoke func
with different values for arg1
.
from functools import singledispatch@singledispatchdef func(arg1, arg2):print("default implementation of func - ", arg1, arg2)@func.register(str)def func_impl_1(arg1, arg2):print("Implementation of func with first argument as string - ", arg1, arg2)@func.register(int)@func.register(float)def func_impl_2(arg1, arg2):print("Implementation of func with first argument as integer or float - ", arg1, arg2)func(1, "hello")func("test", "hello")func(1.34, "hi")
We obtain all the different implementations registered to a function using the registry
attribute attached. It’s a dictionary that stores the various implementations for other types.
Line 11-14: We register multiple types for the exact implementation of func
.
Line 17: We print the different implementations and types registered for func
using the registry
attribute.
Line 20: We obtain the str
implementation of func
as registry
is a dictionary.
from functools import singledispatch@singledispatchdef func(arg1, arg2):print("default implementation of func - ", arg1, arg2)@func.register(str)def func_impl_1(arg1, arg2):print("Implementation of func with first argument as string - ", arg1, arg2)@func.register(int)@func.register(float)def func_impl_2(arg1, arg2):print("Implementation of func with first argument as integer or float - ", arg1, arg2)print("The different implementations of the func are")print(func.registry)print("String implementation:")print(func.registry[str])