Pythonic function composition

Michael J. Fromberger Michael.J.Fromberger at Clothing.Dartmouth.EDU
Mon Oct 25 12:32:12 EDT 2004


In article <10nq3c4j3lst8e2 at corp.supernews.com>,
 "Alan G Isaac" <aisaac at american.edu> wrote:

> Given a list of functions, it seems there must be a
> Pythonic approach to composition.  Something like
> 
> def compose(fns): return lambda x: reduce(lambda f,g: f(g),fns)(x)
> 
> This will not work because the argument 'x' is not "inside".
> What is the proper formulation?
> 

If you are only concerned with unary functions, then composition is 
fairly trivial to deal with:

def compose(*fns):
  def id(x): return x
  
  def c2(f, g):
    def h(x): return f(g(x))
    return h
  
  return reduce(c2, fns, id)

However, if you want to deal with functions that may take multiple 
arguments, you must be a little more clever.  Here's one way that seems 
to work okay:

def compose(*fns):
    def id(*args): return args
    
    def box(res):
        if isinstance(res, (list, tuple)):
            return res
        else:
            return (res,)

    def unbox(res):
        if isinstance(res, (list, tuple)) and len(res) == 1:
            return res[0]
        else:
            return res
    
    def c2(f, g):
        def h(*args):
            return unbox(f(*box(g(*args))))
        return h
    
    return reduce(c2, fns, id)

For instance:
  def f1(a, b):
    return (a / b, a % b)

  def f2(a, b):
    return a + b

  def f3(a):
    return a + 2

h = compose(f3, f2, f1)
h(5, 3)
==> 5

This will work, but it's not the most efficient possible solution.  You 
could defer unboxing until the end by defining another intermediate 
function.

Cheers,
-M

-- 
Michael J. Fromberger             | Lecturer, Dept. of Computer Science
http://www.dartmouth.edu/~sting/  | Dartmouth College, Hanover, NH, USA



More information about the Python-list mailing list