What is Singledispatch descriptor in Functools module in Python?
Overview
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.
The singledispatch decorator
The 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.
Type annotation for function overloading
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.
Example
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")
Explanation
-
Line 1: We import the
singledispatchfrom thefunctoolsmodule. -
Line 3–5: We define a function named
functo accept two arguments,arg1andarg2. Thefuncis decorated with asingledispatchdecorator. This is the default implementation of the functionfunc. -
Line 7-9: We register the first implementation of
funcfor whicharg1shall be of type string. Here,arg1is annotated withstringas its type. -
Line 11-13: We register the second implementation of
funcfor whicharg1shall be of typeint. Here,arg1is annotated withintas its type. -
Line 15-17: We invoke
funcwith different values forarg1. -
Line 17: We execute the default implementation of the function as there is no implementation of
funcfor thefloattype.
The register attribute for function overloading
If the code doesn’t use type annotations, the relevant type argument can be passed explicitly to the decorator via register.
Example
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")
Explanation
-
Line 1: We’ll import the
singledispatchfrom thefunctoolsmodule. -
Line 3-5: A function named
funcis defined that accepts two arguments,arg1andarg2. Thefuncis decorated with asingledispatchdecorator. This is the default implementation of the functionfunc. -
Line 7–9: We register the first implementation of
funcfor whicharg1should be of type string. This is done by providingstrto theregisterattribute. -
Line 11-13: We register the second implementation of
funcfor whicharg1should be of typeint. This is done by providingintto theregisterattribute. -
Lines 15-17: We invoke
funcwith different values forarg1. -
Line 17: We execute the default implementation of the function as there is no implementation of
funcfor thefloattype.
Decorator stacking
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
funcwith different values forarg1.
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")
Get all implementations of a function
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
funcusing theregistryattribute. -
Line 20: We obtain the
strimplementation offuncasregistryis 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])