What's the logic behind this particular Python functions composition?

danicotra

Consider the following Python snippet concerning functions composition:

from functools import reduce
def compose(*funcs):
    # compose a group of functions into a single composite (f(g(h(..(x)..)))
    return reduce(lambda f, g: lambda *args, **kwargs: f(g(*args, **kwargs)), funcs)


### --- usage example:
from math import sin, cos, sqrt
mycompositefunc = compose(sin,cos,sqrt)
mycompositefunc(2)

I have two questions:

  1. Can someone please explain me the compose "operational logic"? (How it works?)
  2. Would it be possible (and how?) to obtain the same thing without using reduce for this?

I already looked here, here and here too, my problem is NOT understanding what lambda means or reduce does (I think I got, for instance, that 2 in the usage example will be somewhat the first element in funcs to be composed). What I find harder to understand is rather the complexity of how the two lambdas got combined/nested and mixed with *args, **kwargs here as reduce first argument ...


EDIT:

First of all, @Martijn and @Borealid, thank you for your effort and answers and for the time you are dedicating to me. (Sorry for the delay, I do this in my spare time and not always have a a lot...)

Ok, coming to facts now...

About 1st point on my question:

Before anything, I realized what I didn't really got (but I hope I did now) about those *args, **kwargs variadic arguments before is that at least **kwargs is not mandatory (I say well, right?) This made me understand, for instance, why mycompositefunc(2) works with that only one (non keyword) passed argument.

I realized, then, that the example would work even replacing those *args, **args in the inner lambda with a simple x. I imagine that's because, in the example, all 3 composed functions (sin, cos, sqrt) expect one (and one only) parameter... and, of course, return a single result... so, more specifically, it works because the first composed function expect just one parameter (the following others will naturally get only one argument here, that's the result of the previous composed functions, so you COULDN'T compose functions that expect more than one argument after the first one... I know it's a bit contort but I think you got what I'm trying to explain...)

Now coming to what remains the real unclear matter for me here:

lambda f, g: lambda *args, **kwargs: f(g(*args, **kwargs))

How does that lambda nesting "magic" works?

With all the great respect you deserve and I bear you, it seems to me like both of you are wrong coming to the conclusion the final result shall be: sqrt(sin(cos(*args, **kw))). It actually can't be, the order of appliance of the sqrt function is clearly reversed: it's not the last to be composed but the first.

I say this because:

>>> mycompositefunc(2)
0.1553124117201235

its result is equal to

>>> sin(cos(sqrt(2)))
0.1553124117201235

whereas you get an error with

>>> sqrt(sin(cos(2)))
[...]
ValueError: math domain error

(that's due to trying to squareroot a negative float)

#P.S. for completeness:

>>> sqrt(cos(sin(2)))
0.7837731062727799

>>> cos(sin(sqrt(2)))
0.5505562169613818

So, I understand that the functions composition will be made from the last one to the first ( i.e. : compose(sin,cos,sqrt) => sin(cos(sqrt(x))) ) but the "why?" and how does that lambda nesting "magic" works? still remains a bit unclear for me... Help/Suggestions very appreciated!

On 2nd point (about rewriting compose without reduce)

@Martijn Pieters: your first compose (the "wrapped" one) works and returns exactly the same result

>>> mp_compfunc = mp_compose(sin,cos,sqrt)
>>> mp_compfunc(2)
0.1553124117201235

The unwrapped version, instead, unfortunately loops until RuntimeError: maximum recursion depth exceeded ...

@Borealid: your foo/bar example will not get more than two functions for composition but I think it was just for explanations not intended for answering to second point, right?

Martijn Pieters

The *args, **kw syntax in both the lambda signature and call syntax are the best way to pass on arbitrary arguments. They accept any number of positional and keyword arguments and just pass those on to a next call. You could write the result of the outer lambda as:

def _anonymous_function_(*args, **kw):
    result_of_g = g(*args, **kw)
    return f(result_of_g)
return _anonymous_function

The compose function can be rewritten without reduce() like this:

def compose(*funcs):
    wrap = lambda f, g: lambda *args, **kw: f(g(*args, **kw))
    result = funcs[0]
    for func in funcs[1:]:
        result = wrap(result, func)
    return result

This does the exact same thing as the reduce() call; call the lambda for the chain of functions.

So, the first two functions in the sequence are sin and cos, and these are replaced by:

lambda *args, **kw: sin(cos(*args, **kw))

This then is passed to the next call as f, with sqrt g, so you get:

lambda *args, **kw: (lambda *args, **kw: sin(cos(*args, **kw)))(sqrt(*args, **kw)))

which can be simplified to:

lambda *args, **kw: sin(cos(sqrt(*args, **kw)))

because f() is a lambda that passes its arguments to the nested sin(cos()) call.

In the end, then, you have produced a function that calls sqrt(), the result of which is passed to cos(), and the output of that is then passed to sin(). The *args, **kw lets you pass in any number of arguments or keyword arguments, so the compose() function can be applied to anything than is callable, provided that all but the first function takes just one argument, of course.

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related

From Dev

What's the logic behind this particular Python functions composition?

From Dev

What's the logic behind Python's hash function order?

From Dev

What is the logic behind the algorithm

From Dev

What's the logic behind this small c++ code?

From Dev

What's the logic behind how React Router processes onEnter?

From Dev

What's the logic behind how React Router processes onEnter?

From Dev

what's the logic behind 'default credentials' to authenticate bigQuery client

From Dev

What's the logic behind yielding scrapy.http.Request?

From Dev

What is the logic behind string removal?

From Dev

What logic is behind NaN evaluation?

From Dev

What is the logic behind this prolog code?

From Dev

What is the logic behind this how it is working?

From Dev

What are the decisions behind type availability for particular platform?

From Dev

What is the logic behind this simple hash function?

From Java

What is the logic behind the "using" keyword in C++?

From Dev

What is the logic behind The order of DockPanel children elements

From Dev

What is the logic behind when JavaScript throws a ReferenceError?

From Dev

Android SQLite -- What is the logic behind using whereArgs

From Dev

What is the logic behind the sort method used with a compareFunction?

From Dev

What is the logic behind the expression cat > foo << "wordofchoice"

From Dev

What is the logic behind the pin diagram of Ethernet cables?

From Dev

What is the logic of auto generated comments behind Bitbucket?

From Dev

What is The logic behind chatting android app

From Dev

What is the logic behind Html.DisplayNameFor

From Dev

Logic behind finding out Pascal's Triangle

From Dev

Logic behind finding out Pascal's Triangle

From Java

Python multiprocessing: understanding logic behind `chunksize`

From Dev

Python multiprocessing: understanding logic behind `chunksize`

From Dev

Logic behind Python reversing a slice with index and step

Related Related

  1. 1

    What's the logic behind this particular Python functions composition?

  2. 2

    What's the logic behind Python's hash function order?

  3. 3

    What is the logic behind the algorithm

  4. 4

    What's the logic behind this small c++ code?

  5. 5

    What's the logic behind how React Router processes onEnter?

  6. 6

    What's the logic behind how React Router processes onEnter?

  7. 7

    what's the logic behind 'default credentials' to authenticate bigQuery client

  8. 8

    What's the logic behind yielding scrapy.http.Request?

  9. 9

    What is the logic behind string removal?

  10. 10

    What logic is behind NaN evaluation?

  11. 11

    What is the logic behind this prolog code?

  12. 12

    What is the logic behind this how it is working?

  13. 13

    What are the decisions behind type availability for particular platform?

  14. 14

    What is the logic behind this simple hash function?

  15. 15

    What is the logic behind the "using" keyword in C++?

  16. 16

    What is the logic behind The order of DockPanel children elements

  17. 17

    What is the logic behind when JavaScript throws a ReferenceError?

  18. 18

    Android SQLite -- What is the logic behind using whereArgs

  19. 19

    What is the logic behind the sort method used with a compareFunction?

  20. 20

    What is the logic behind the expression cat > foo << "wordofchoice"

  21. 21

    What is the logic behind the pin diagram of Ethernet cables?

  22. 22

    What is the logic of auto generated comments behind Bitbucket?

  23. 23

    What is The logic behind chatting android app

  24. 24

    What is the logic behind Html.DisplayNameFor

  25. 25

    Logic behind finding out Pascal's Triangle

  26. 26

    Logic behind finding out Pascal's Triangle

  27. 27

    Python multiprocessing: understanding logic behind `chunksize`

  28. 28

    Python multiprocessing: understanding logic behind `chunksize`

  29. 29

    Logic behind Python reversing a slice with index and step

HotTag

Archive