Skip to content

Instantly share code, notes, and snippets.

@NotBobTheBuilder
Last active February 21, 2017 07:41
Show Gist options
  • Save NotBobTheBuilder/08bad9d2554fcde371fd5d7aae56c644 to your computer and use it in GitHub Desktop.
Save NotBobTheBuilder/08bad9d2554fcde371fd5d7aae56c644 to your computer and use it in GitHub Desktop.
Python dynamic dispatch
from collections import OrderedDict, namedtuple
from functools import partial
class Dispatcher(object):
def __init__(self):
self.handlers = OrderedDict()
def match(self, *args):
def match_inner(handler_fn):
self.handlers[args] = handler_fn
return self
return match_inner
def on(self, *args):
def on_inner(handler_fn):
# We need an outer closure here as a reference to `a`
# in a single closure would reference the last value of
# `a` after the for loop.
matchfn = lambda a: lambda v: isinstance(v, a)
matches = [matchfn(a) for a in args]
return self.match(*matches)(handler_fn)
return on_inner
def __call__(self, *args, **kwargs):
if len(args) == 0:
raise ValueError('No arguments to match on')
for matches, handler in self.handlers.iteritems():
if any(m(args[-1]) for m in matches):
return handler(*args, **kwargs)
raise ValueError('No handlers found for value {}'.format(args[-1]))
def __get__(self, obj, objtype):
return partial(self.__call__, obj)
# Simple example:
foo = Dispatcher()
@foo.on(int, float)
def foo(x):
return "Got a number! {}".format(x)
@foo.on(str, unicode)
def foo(x):
return "Got some text! {}".format(x)
@foo.on(dict)
def foo(x):
return "Wow a dict {}".format(x)
print(foo(1))
print(foo(1.0))
print(foo('abc'))
print(foo(u'def'))
print(foo({}))
# Class Example:
Leaf = namedtuple('Leaf', 'l r')
tree = Leaf(Leaf(Leaf(1, 2), Leaf("3", 4.0)), [5])
class TreeVisitor(object):
visit = Dispatcher()
@visit.on(Leaf)
def visit(self, value):
return self.visit(value.l) + ' || ' + self.visit(value.r)
@visit.on(int)
def visit(self, value):
return 'i' + str(value)
@visit.on(str)
def visit(self, value):
return 's"' + value + '"'
@visit.on(float)
def visit(self, value):
return 'f' + str(value)
@visit.on(list)
def visit(self, value):
return str(value)
print(TreeVisitor().visit(tree))
@NotBobTheBuilder
Copy link
Author

Hat tip to Chris Lamb for the inspiration http://chris-lamb.co.uk/posts/visitor-pattern-in-python

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment