[Python-Dev] functools.compose to chain functions together
Steven D'Aprano
steve at pearwood.info
Mon Aug 17 09:43:52 CEST 2009
On Mon, 17 Aug 2009 08:10:16 am Martin v. Löwis wrote:
> I don't think he did. Comparing it to the one obvious solution (use
> a lambda expression), his only reasoning was "it is much easier to
> read". I truly cannot believe that a compose function would be
> easier to read to the average Python programmer: if you have
>
> def foo(data):
> return compose(a, b(data), c)
>
> what would you expect that to mean?
foo is a factory function that, given an argument `data`, generates a
function b(data), then composes it with two other functions a and c,
and returns the result, also a function.
> Please rewrite it as a regular
> Python expression, preferably without looking at the patch that
> has been proposed first. I bet there is a 50% chance that you get
> it wrong (because there are two possible interpretations).
But surely only one of them will agree with the standard definition of
function composition. Both Mathworld and Wikipedia agree that f∘g(x)
is equivalent to f(g(x)):
http://mathworld.wolfram.com/Composition.html
http://en.wikipedia.org/wiki/Function_composition
and I don't see any reason why a compose() function shouldn't do the
same.
(Aside: how do I look at the patch? The only link I have is here:
http://mail.python.org/pipermail/patches/2007-February/021687.html
but I can't see how to get to the patch from there.)
foo could be written as:
def foo(data):
return lambda *args, **kwargs: a(b(data)(c(*args, **kwargs)))
Or without lambda:
def foo(data):
def composed(*args, **kwargs):
return a(b(data)(c(*args, **kwargs)))
return composed
This soon gets unwieldy:
def foo(arg1, arg2, arg3):
return compose(
f, g, h, factory(arg1), factory(arg2), factory(arg3)
)
versus
def foo(arg1, arg2, arg3):
return lambda *a, **kw: (
f(g(h(factory(arg1)(factory(arg2)(factory(arg3)(*a, **kw))))))
)
but presumably composing six functions is rare.
A further advantage of compose() is that one could, if desired,
generate a sensible name and doc string for the returned function.
Depends on how heavyweight you want compose() to become.
I think the compose() version is far more readable and understandable,
but another factor is the performance cost of the generated function
compared to a hand-made lambda.
For the record, Haskell makes compose a built-in operator:
http://www.haskell.org/haskellwiki/Function_composition
It doesn't appear to be standard in Ruby, but it seems to be commonly
requested, and a version is on Facets:
http://facets.rubyforge.org/apidoc/api/core/classes/Proc.html#M000161
--
Steven D'Aprano
More information about the Python-Dev
mailing list