I think Guide is thinking about typing and whatnot a lot lately, now with a post one multimethods, which mostly the same as double-dispatch. I.e., you choose a function implementation based on the type of the arguments.
However, Guido uses a global registry of functions by name. I like Phillip's technique for generic functions, which is to give these a functions a richer interface that includes a decorator. Here's an example of Guido's code converted to that:
@multimethod def div(a, b): "div a by b" return a / b class rational(object): def __init__(self, num, denom): self.num, self.denom = num, denom def __repr__(self): return '%s/%s' % (self.num, self.denom) @div.types(rational, int) def div_rat_int(r, i): return rational(r.num, r.denom*i) @div.types(int, rational) def div_int_rat(i, r): return rational(r.denom * i, r.num) @div.types(rational, float) def div_rat_float(r, f): return r.num / (r.denom*f) @div.types(float, rational) def div_float_rat(f, r): return f * r.denom / r.num @div.types(rational, rational) def div_rat_rat(r1, r2): return rational(r1.num * r2.denom, r1.denom * r2.num)
Here's how it looks:
>>> half = rational(1, 2) >>> half 1/2 >>> third = rational(1, 3) >>> div(half, third) 3/2 >>> div(half, 2) 1/4 >>> div(half, 0.5) 1.0 >>> div(1, 2) 0 >>> div(1, half) 2/1
And here's the implementation (it's actually simpler than Guido's):
class multimethod(object): def __init__(self, default): self.default = default self.name = default.__name__ self.__doc__ = default.__doc__ self.typemap = {} def __call__(self, *args): types = tuple(arg.__class__ for arg in args) # a generator expression! function = self.typemap.get(types, self.default) return function(*args) def types(self, *types): def decorator(func): if types in self.typemap: raise TypeError("duplicate registration") self.typemap[types] = func if func.__name__ == self.name: # to avoid confusion by overwriting the multimethod function return self else: return func return decorator
The module is located online at http://svn.colorstudy.com/home/ianb/recipes/multimethod.py
Update: some people have mistakenly called this implementation a generic function, or a reimplementation as a generic function. It's not generic (aka predicate-dispatch) functions at all -- those are much more general (and powerful), and this version is just a slightly different factoring of Guido's code. Thankfully Bob Ippolito does it right and shows how to implement multimethods with generic functions.
Just an FYI, but if the user imports div "as" another name, this code won't catch that and return self. The technique I use in PyProtocols is to check whether the decorated function's __name__ in the caller's f_locals is self. So you could do something like:
if sys._getframe(1).f_locals.get(func.__name__) is self: return self else: return funcThen it doesn't depend on the original generic function's definition being only accessible via its original name, in order to prevent such confusion.