The recipe presents a simple decorator for function overloading in python. The @overloaded function searches for the first overloads that doesn't raise TypeError when called. Overloads are added to the overloads list by using the @func.overload_with decorator. The order of function definition determines which function gets tried first and once it founds a compatible function, it skips the rest of the overloads list.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
#!/usr/bin/env python from functools import wraps def overloaded(func): @wraps(func) def overloaded_func(*args, **kwargs): for f in overloaded_func.overloads: try: return f(*args, **kwargs) except TypeError: pass else: # it will be nice if the error message prints a list of # possible signatures here raise TypeError("No compatible signatures") def overload_with(func): overloaded_func.overloads.append(func) return overloaded_func overloaded_func.overloads = [func] overloaded_func.overload_with = overload_with return overloaded_func ############# @overloaded def foo(): print 'foo() without args' pass @foo.overload_with def _(n): # note that, like property(), the function's name in # the "def _(n):" line can be arbitrary, the important # name is in the "@overloads(a)" line print 'foo() with one argument' pass foo() foo(4) foo(4, 5) # ERROR: no matching signature
In the past, tricks using default arguments such as:
def foo(n=None): if n is None: n = 10 return n + 20
has been used and has become a popular idiom; this decorator is meant to replace a subset of that idiom.
The use of function overloading clearly separates each signature's code and completely prevents code in one signature from interfering code in another signature. It also makes for smaller functions body, as each function only cares about its own signature.
Limitations: The decorator doesn't do type-checking; it can only classifies overloads by the number of arguments. An alternate implementations could do some type checking so it can classify by the type of the arguments as well.
Known Issues: The decorator relies on catching TypeError, therefore if the underlying code raises TypeError... nobody knows what might happen.
This version is the simpler recipe for the type-checking version here: http://code.activestate.com/recipes/577065-type-checking-function-overloading-decorator/