Recipe16.5.Composing Functions


Recipe 16.5. Composing Functions

Credit: Scott David Daniels

Problem

You need to construct a new function by composing existing functions (i.e., each call of the new function must call one existing function on its arguments, then another on the result of the first one).

Solution

Composition is a fundamental operation between functions and yields a new function as a result. The new function must call one existing function on its arguments, then another on the result of the first one. For example, a function that, given a string, returns a copy that is lowercase and does not have leading and trailing blanks, is the composition of the existing string.lower and string.strip functions. (In this case, it does not matter in which order the two existing functions are applied, but generally, it could be important.)

A closure (a nested function returned from another function) is often the best Pythonic approach to constructing new functions:

def compose(f, g, *args_for_f, **kwargs_for_f):     ''' compose functions.  compose(f, g, x)(y) = f(g(y), x)) '''     def fg(*args_for_g, **kwargs_for_g):         return f(g(*args_for_g, **kwargs_for_g), *args_for_f, **kwargs_for_f)     return fg def mcompose(f, g, *args_for_f, **kwargs_for_f):     ''' compose functions.  mcompose(f, g, x)(y) = f(*g(y), x)) '''     def fg(*args_for_g, **kwargs_for_g):         mid = g(*args_for_g, **kwargs_for_g)         if not isinstance(mid, tuple):             mid = (mid,)         return f(*(mid+args_for_f), **kwargs_for_f)     return fg

Discussion

The closures in this recipe show two styles of function composition. I separated mcompose and compose because I think of the two possible forms of function composition as being quite different, in mathematical terms. In practical terms, the difference shows only when the second function being composed, g, returns a tuple. The closure returned by compose passes the result of g as f's first argument anyway, while the closure returned by mcompose treats it as a tuple of arguments to pass along. Any extra arguments provided to either compose or mcompose are treated as extra arguments for f (there is no standard functional behavior to follow here):

compose(f, g, x)(y) = f(g(y), x) mcompose(f, g, x)(y) = f(*g(y), x)

As in currying (see Recipe 16.4), this recipe's functions are for constructing functions from other functions. Your goal in so doing should be clarity, since no efficiency is gained by using these functional forms.

Here's a quick example for interactive use:

parts = compose(' '.join, dir)

When called on a module object, the callable we just bound to name parts gives you an easy-to-view string that lists the module's contents.

See Also

Recipe 16.4 for an example of "curry"ing (i.e., associating parameters with partially evaluated functions).



Python Cookbook
Python Cookbook
ISBN: 0596007973
EAN: 2147483647
Year: 2004
Pages: 420

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net