Multiple Dispatch in Python
I recently read Functional Programming in Python which mentions a library which adds multiple dispatch to Python. One of my biggest hobbies is creating things from scratch, as it really helps me get a better understanding of how they work, so I thought I’d see if I couldn’t figure out how to do it myself.
Here’s what I whipped up. I’ll add a disclaimer that in this form, it
doesn’t work well for methods or class methods, as they both take a
special argument in the first position (namely self
or
cls
), and it doesn’t accept keyword arguments. At some
stage I’ll try to make it a bit more rigorous.
class Dispatch:
_existing = {}
def __init__(self, *types):
self.types = types
def __call__(self, fn):
name = fn.__qualname__
if name not in self._existing:
table = {}
def dispatcher(*args):
types = tuple(map(type, args))
try:
overload = table[types]
except KeyError:
try:
overload = table[(True,)]
except KeyError:
raise NotImplemented from None
return overload(*args)
self._existing[name] = (table, dispatcher)
(table, dispatcher) = self._existing[name]
table[self.types] = fn
return dispatcher
Once you tuck this away in a library somewhere where you don’t have to look at it, you can use it to write some pretty elegant code. See the following implementation of the flatten function.
@Dispatch(list)
def flatten(elems):
return reduce(add, map(flatten, elems), [])
@Dispatch(True)
def flatten(elem):
return [elem]