Generic Function in Python with Singledispatch
Imagine, you can write different implementations of a function of the same name in the same scope, depending on the types of arguments. Wouldn’t it be great? Of course, it would be. There is a term for this. It is called “Generic Function”. Python recently added support for generic function in Python 3.4 (PEP 443). They did this to the
functools module by adding
What is Singledispatch?
At this point, you may be wondering what is
singledispatch. Okay, let’s go with generic function again.
A generic function is composed of multiple functions implementing the same operation for different types. Which implementation should be used during a call is determined by the dispatch algorithm. When the implementation is chosen based on the type of a single argument, this is known as single dispatch.
In Python, implementation is chosen based on the type of the first argument of function. So in simple, you define a default function and then register additional versions of that functions depending on the type of the first argument.
Singledispatch in Action
singledispatch in action. There are few steps for writing a generic function with
- Define an default or fallback function with
@singledispatchdecorator. It’s our generic function.
- Then, Register additional implementations by passing the type in
register()attribute of the generic function. It’s a decorator, so you decorate your implementations like this
@function_name.register(type). You can also register lambdas and pre-existing functions.
Now, we will implement a generic function called
fprint, which will print something in a formatted way based on the type. For
list it will print index and value along with type of value and for
dict it will print key-value pair along with their type etc. By default it will print the passed argument along with it’s type. Let’s define our default function first.
I am not going to explain the implementation. It’s fairly basic. It’s the default or fallback implementation of our generic function. We define a function and decorate it with
@singledispatch decorator. If there is no registered implementation for a specific type, its method resolution order is used to find a more generic implementation. The original function decorated with
@singledispatch is registered for the base object type, which means it is used if no better implementation is found.
Remember the next step? Now, it’s time for registering the overloaded implementations. Let’s implement for
In case you are wondering why I am using
_ as name. It’s because, I want only one generic function. If you give it a name, you will get the option to use this specific function independently. Assume that we gave a name to the above function
list_print and didn’t decorate with
@fprint.register(list). Now, we can use
fprint.register() as function like this
fprint.register(list, list_print). We can also stack more than one decorator for multiple type just like this.
We almost finished our generic function except for
dict type. Here is our full code along with implementation for
Summing It Up
As you can see, Python provides a clear way to define and extend generic functions. It opens some interesting possibilities to refactor your code. If you are interested to learn more about
singledispatch, you should check out PEP 443 and
Note: This post was previously published on my blog.