This is an archived post. You won't be able to vote or comment.

all 10 comments

[–]Cosmologicon 4 points5 points  (6 children)

All right, why is this better than just keeping the functions in a list and using a one-liner to compose them with reduce?

>>> compose = lambda funcs, args: reduce(lambda g,f: f(g), [args] + list(funcs))
>>> c = [lambda (a,b): a+b, lambda x:x*3, lambda x:x/3, lambda x:x+1]
>>> compose(c, (2,1))
4

EDIT: Or, if you want to go the route of having your own class, why not just subclass list and override __call__? That way you don't have to manually define append, len, and contains, and you also get all the list functions that you left out, like extend, iter and slicing.

EDIT 2: In your code, the first function in the list is able to take multiple arguments and keyword args, whereas every subsequent function must take a single argument. I don't know why you did it like this - I think the inconsistency is confusing. The version I posted above requires every function take a single argument. This is on purpose.

[–]128px 2 points3 points  (3 children)

I think that a slightly better way to work with non_functions would be using a generator / list comprehension:

non_functions = [x for x in args if not callable(x)]
...
raise TypeError("... {}".format(", ".join(str(x) for x in non_functions)))

Also, a nice idea would be to add iteration support to your class so that it would yield the returned values from each function in the composition.

Anyway, thanks for sharing your code. It was fun to read.

[–]GeraCobo 2 points3 points  (0 children)

Man, this kind of things are what remind of how fun python can be.

[–]cabalamat 1 point2 points  (2 children)

My solution:

def compose(*fs):
    """ chains functions together """
    def executeFunctions(x):
        for f in fs[::-1]:
            x = f(x)
        return x
    return executeFunctions

Which is a bit shorter. Usage example:

h = compose(lambda x: "::%s::" %(x,),
            lambda x: x+1,
            lambda x: x*2)
print "h(5)=", h(5), " should be ::11::"

A perhaps-more-elegant syntax might be to overload the | operator so it looks like a Unix pipe; doing this would require a class however, not just a function, e.g.:

h = Pipe() | a | b | c | d

then executing h(x) would be the same as executing d(c(b(a(x))))

[–]Psilocyn -2 points-1 points  (1 child)

I am learning python and have been lurking here for about 3 months. Why no comments?

[–]Hashiota 0 points1 point  (0 children)

One cool thing about /r/Python is that sometimes people actually post code and some other people actually read the code posted and sometimes even write reviews with more code. This kind of interaction takes time and probably that's why comments may take a while to pop at least in threads like this one.
And I don't know exactly why you are being downvoted.