#458 · Functions · Expert · 230 XP · about 50 min
functools.singledispatch chooses an implementation by the type of the first argument. Build multidispatch, which looks at the types of all the positional arguments.
- multidispatch(func) returns a dispatcher: an object you call like a function, which has a method register(*types). - register(*types) returns a decorator. The decorator records the function it decorates as the implementation for those types (one class per positional argument) and returns that function unchanged. Registering the same types again replaces the old implementation. When the dispatcher is called with positional arguments args:
type(arg).__mro__: 0 for the argument's own class, 1 for its parent, and so on, following the method resolution order.args and returns its result.func with args and returns its result.TypeError.class Shape: pass class Circle(Shape): pass class Square(Shape): pass multidispatch def overlap(a, b): return 'unknown' overlap.register(Shape, Shape) def _(a, b): return 'bounding boxes' overlap.register(Circle, Circle) def _(a, b): return 'compare radii' overlap.register(Circle, Shape) def _(a, b): return 'circle first' overlap.register(Shape, Circle) def _(a, b): return 'circle second' overlap(Circle(), Circle()) # returns 'compare radii' distances (0, 0) overlap(Circle(), Square()) # returns 'circle first' (0, 1) beats (1, 1) overlap(Square(), Square()) # returns 'bounding boxes' overlap(Square(), 5) # returns 'unknown' nothing matches
Without the (Circle, Circle) registration, overlap(Circle(), Circle()) would raise TypeError: (Circle, Shape) has distances (0, 1), (Shape, Circle) has (1, 0), and neither is as close as the other in both places.
First solves only, ranked by stars and then time.