Pythonic function composition

A

Alan G Isaac

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?

Thanks,
Alan Isaac
 
P

Peter Otten

Alan said:
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?

You need to pass a function that makes a function (a "factory") to reduce():
fns = [lambda x: x+2, lambda x: x*2, lambda x: x*x]
def compose(f, g):
.... def fog(x):
.... return f(g(x))
.... return fog
....10

The same with lambdas:
10

Peter
 
O

Oliver Fromme

Alan G Isaac said:
> 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?

There are probably several ways to do it.
The following is the one which come to my mind first.

The trick is to first define a function that composes
two functions, and then use reduce() to apply it to an
arbitrary number of functions.
.... def h (x):
.... return f(g(x))
.... return h
........ return reduce(compose2, fns)
....

Some testing:
27

There's no need to juggle with lambda in this case.
Lambda has its uses, but this isn't one of them.

Best regards
Oliver
 
A

Alan G Isaac

Cool. That gets me there. I think
def compose(fns) : return reduce(lambda f, g: lambda x: f(g(x)), fns)
does exactly what I want.

Thanks,
Alan Isaac
 
A

Alan G Isaac

Oliver Fromme said:
There's no need to juggle with lambda in this case.
Lambda has its uses, but this isn't one of them.

I kind of like the lambda version (see Peter's post).
But maybe it is more opaque.

Thanks,
Alan
 
M

Michael J. Fromberger

"Alan G Isaac said:
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
 
L

Lonnie Princehouse

How about some recursion?

def compose(f, *fns):
if not fns:
return f
else:
return lambda x: f(compose(*fns)(x))

....
1.0

....or you could try it this way, which makes some assumptions about
function names, but will possibly run faster:

def compose(*fns):
fnames = [f.__name__ for f in fns]
expr = "%s(x%s" % ('('.join(fnames),')'*len(fns))
return eval("lambda x: %s" % expr)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,068
Latest member
MakersCBDIngredients

Latest Threads

Top