|I l@ve RuBoard|
15.8 Associating Parameters with a Function (Currying)
Credit: Scott David Daniels, Ben Wolfson, Nick Perkins, and Alex Martelli
You need to tweak a function (or other callable) to get another callable with fewer formal arguments, keeping the original arguments' given values fixed (i.e., you need to curry a callable to make another).
Curry is not just a spice used in Asian cuisine; it's also an important technique in Python and other programming languages:
class curry: def _ _init_ _(self, fun, *args, **kwargs): self.fun = fun self.pending = args[:] self.kwargs = kwargs.copy( ) def _ _call_ _(self, *args, **kwargs): if kwargs and self.kwargs: kw = self.kwargs.copy( ) kw.update(kwargs) else: kw = kwargs or self.kwargs return self.fun(*(self.pending + args), **kw)
Popular in functional programming, currying is a way to bind some of the function's arguments and wait for the rest of the arguments to show up later. Currying is named in honor of Haskell Curry, a mathematician who laid some of the cornerstones in the theory of formal systems and processes. The curry function defined in this recipe is called with a callable and some or all of the arguments to the callable. The curry function returns a function that takes subsequent parameters as arguments, and curry calls the original with all of those parameters. This recipe uses a class instance to hold the curried parameters until they're needed. For example:
double = curry(operator.mul, 2) triple = curry(operator.mul, 3)
Currying is often implemented with lambda forms, but a dedicated class such as the one provided in this recipe is clearer and more readable. However, lambda does have the advantage that the arguments can be given in any order. If you have such needs and prefer to use explicit currying classes (or functions) rather than lambda, you may have to code other dedicated adapters for the purpose of renaming or reordering arguments.
A typical use of curry is to construct callback functions for GUI operations. When the operation does not merit a new function name, curry can be useful in creating these little functions. For example, this can be the case with commands for Tkinter buttons:
self.button = Button(frame, text='A', command=curry(transcript.append, 'A'))
Recipe 9.2 shows a specialized subset of curry functionality intended to produce callables that require no arguments, which are often needed for such GUI-callback usage. However, the recipe shown here is vastly more flexible, without substantial extra cost in complexity or performance.
Currying can also be used interactively to make versions of your functions with debugging-appropriate defaults or initial parameters filled in for your current case. For example, database debugging work might begin by setting:
Connect = curry(ODBC.Connect, dsn='MyDataSet')
Another example of the use of curry in debugging is wrapping methods:
def report(originalFunction, name, *args, **kw): print "%s(%s)"%(name, ', '.join(map(repr, args) + [k+'='+repr(kw[k]) for k in kw.keys( )]) result = originalFunction(*args, **kw) if result: print name, '==>', result return result class Sink: def write(self, text): pass dest = Sink( ) dest.write = curry(report, dest.write, 'write') print >>dest, 'this', 'is', 1, 'test'
If you are creating a function for regular use, and there is a good choice for a name, the def fun form of function definition is usually more readable and more easily extended. As you can see from the implementation, no magic happens to specialize the function with the provided parameters. curry should be used when you feel the code is clearer with its use than without. Typically, this will emphasize that you are only providing parameters to a commonly used function, not providing separate processing.
BlueWindow = curry(Window, background="blue")
Of course, BlueWindow._ _class_ _ is still Window, not a subclass. But if you're changing only default parameters, not behavior, currying is arguably more appropriate than subclassing anyway. And you can still pass additional parameters to the curried constructor.
An alternative implementation of currying uses lexically nested scopes, available in Python 2.2 (or 2.1 with from _ _future_ _ import nested_scopes). The most general way to use nested scopes for currying is something like:
def curry(*args, **kwds): def callit(*moreargs, **morekwds): kw = kwds.copy( ) kw.update(morekwds) return args(*(args[1:]+moreargs), **kw) return callit
This curries positional arguments from the left and gives named arguments specified at call time precedence over those specified at currying time, but these policies are clearly easy to alter. This version using nested scopes rather than a class is more general, because it avoids unintentionally capturing certain argument names, which is inevitable with the class approach. For example, in the class-based solution in the recipe, imagine needing to curry callable with a keyword argument fun=23.
15.8.4 See Also
Recipe 9.2 shows a specialized subset of the curry functionality that is specifically for GUI callbacks.
|I l@ve RuBoard|