Is there a function that applies list of functions to a value?

A

AdamKal

Hi,

From time to time I have to apply a series of functions to a value in such a way:

func4(func3(func2(func1(myval))))

I was wondering if there is a function in standard library that would take a list of functions and a initial value and do the above like this:

func_im_looking_for([func1, func2, func3, func4], myval)

I looked in itertools but nothing seamed to do the job. This seams like something vary obvious that was needed many times elsewhere so maybe you could help me?
 
T

Thomas Rachel

Am 2013-08-28 14:52 schrieb AdamKal:
Hi,

From time to time I have to apply a series of functions to a value in such a way:

func4(func3(func2(func1(myval))))

I was wondering if there is a function in standard library that would take a list of functions and a initial value and do the above like this:

func_im_looking_for([func1, func2, func3, func4], myval)

I looked in itertools but nothing seamed to do the job. This seams like something vary obvious that was needed many times elsewhere so maybe you could help me?

reduce(lambda x, f: f(x), funcs, myval)

would do the job.


Thomas
 
J

Jussi Piitulainen

AdamKal said:
Hi,

From time to time I have to apply a series of functions to a value
in such a way:

func4(func3(func2(func1(myval))))

I was wondering if there is a function in standard library that
would take a list of functions and a initial value and do the above
like this:

func_im_looking_for([func1, func2, func3, func4], myval)

I looked in itertools but nothing seamed to do the job. This seams
like something vary obvious that was needed many times elsewhere so
maybe you could help me?

If you can have things backwards, or have func_im_looking_for reverse
things first, you can do it this way:

from functools import reduce

def funcall(arg, fun): return fun(arg)

def func1(arg): return 'f1({})'.format(arg)
def func2(arg): return 'f2({})'.format(arg)
def func3(arg): return 'f3({})'.format(arg)

# prints: f1(f2(f3(3.14)))
print(reduce(funcall, (func3, func2, func1), 3.14))
 
T

Tim Chase

From time to time I have to apply a series of functions to a value
in such a way:

func4(func3(func2(func1(myval))))

I was wondering if there is a function in standard library that
would take a list of functions and a initial value and do the above
like this:

func_im_looking_for([func1, func2, func3, func4], myval)

At least in Py2.x you can use reduce:

reduce(lambda value, fn: fn(value), [list_of_functions], myval)

I believe it was moved into functools.reduce() in Py3.x meaning you
might want to do something like

try:
reduce # see if it's already in __builtins__
except NameError: # running Py3...
from functools import reduce

or

try:
from functools import reduce
except ImportError:
pass # it should already be in __builtins__ for pre-2.6

at the top to make sure it's in your global namespace.

-tkc
 
I

ishish

Am 28.08.2013 13:52, schrieb AdamKal:
Hi,

From time to time I have to apply a series of functions to a value in
such a way:

func4(func3(func2(func1(myval))))

I was wondering if there is a function in standard library that would
take a list of functions and a initial value and do the above like
this:

func_im_looking_for([func1, func2, func3, func4], myval)

I looked in itertools but nothing seamed to do the job. This seams
like something vary obvious that was needed many times elsewhere so
maybe you could help me?

You could try somthing like:


myval = 'whatever'

for i in range(1,4):
print eval("func%s(%s)" % (i, myval))
 
J

Jussi Piitulainen

Tim said:
From time to time I have to apply a series of functions to a value
in such a way:

func4(func3(func2(func1(myval))))

I was wondering if there is a function in standard library that
would take a list of functions and a initial value and do the above
like this:

func_im_looking_for([func1, func2, func3, func4], myval)

At least in Py2.x you can use reduce:

reduce(lambda value, fn: fn(value), [list_of_functions], myval)

I notice now that I didn't notice, when I just sent the same solution,
that the function list was already backwards to its intended order of
application, so yes, this should work as wanted.

Sorry.
 
A

AdamKal

Thanks!

I guess this is as simple as it gets then. I was just looking for the "one obvious way to do it".

W dniu środa, 28 sierpnia 2013 15:11:34 UTC+2 użytkownik Tim Chase napisał:
From time to time I have to apply a series of functions to a value
in such a way:



I was wondering if there is a function in standard library that
would take a list of functions and a initial value and do the above
like this:
func_im_looking_for([func1, func2, func3, func4], myval)



At least in Py2.x you can use reduce:



reduce(lambda value, fn: fn(value), [list_of_functions], myval)



I believe it was moved into functools.reduce() in Py3.x meaning you

might want to do something like



try:

reduce # see if it's already in __builtins__

except NameError: # running Py3...

from functools import reduce



or



try:

from functools import reduce

except ImportError:

pass # it should already be in __builtins__ for pre-2.6



at the top to make sure it's in your global namespace.



-tkc
 
C

Chris Angelico

I guess this is as simple as it gets then. I was just looking for the "one obvious way to do it".

The one obvious way to do some things is to post on python-list and
see what comes back :) I love reading over these sorts of threads,
they're good fun.

ChrisA
 
T

Tim Chase

Thanks!

I guess this is as simple as it gets then. I was just looking for
the "one obvious way to do it".

When 3 replies from 3 people all arrive within minutes, each
suggesting reduce(), I'd figure it's the "one obvious way to do
it" :)

-tkc
 
A

AdamKal

W dniu środa, 28 sierpnia 2013 15:43:39 UTC+2 użytkownik Tim Chase napisał:
When 3 replies from 3 people all arrive within minutes, each
suggesting reduce(), I'd figure it's the "one obvious way to do
it" :)

I guess it's at least a good hint ;)


Thanks to all! :)
 
J

Josh English

Reduce tricks are nice, but I prefer clarity sometimes:

def double(x):
return x*2

def add3(x):
return x+3


def compose(*funcs):
for func in funcs:
if not callable(func):
raise ValueError('Must pass callable functions')

def inner(value):
for func in funcs:
value = func(value)
return value

return inner


add_then_double = compose(add3, double)
double_then_add = compose(double, add3)

print add_then_double(1) # prints 8
print double_then_add(1) # prints 5
 
F

fp2161

Hi,



From time to time I have to apply a series of functions to a value in such a way:



func4(func3(func2(func1(myval))))



I was wondering if there is a function in standard library that would take a list of functions and a initial value and do the above like this:



func_im_looking_for([func1, func2, func3, func4], myval)



I looked in itertools but nothing seamed to do the job. This seams like something vary obvious that was needed many times elsewhere so maybe you could help me?


My way is so obvious that it may not be that interesting...

def func4(f1,f2,f3,f4):
def anon(x):
f1(f2(f3(f4(x))))
return anon
 
C

Chris Angelico

My way is so obvious that it may not be that interesting...

def func4(f1,f2,f3,f4):
def anon(x):
f1(f2(f3(f4(x))))
return anon

Or have it return the result of f1. And then, since it's an anonymous
function that simply returns an expression, I'd write it as:

def func4(f1,f2,f3,f4):
return lambda x: f1(f2(f3(f4(x))))

Of course, that's still restricted to precisely four args. Extending
this concept to a variable number of arguments is, uhh, left as an
exercise to the reader. Which will probably end up going back to
reduce(). :)

ChrisA
 
F

fp2161

Or have it return the result of f1. And then, since it's an anonymous

function that simply returns an expression, I'd write it as:



def func4(f1,f2,f3,f4):

return lambda x: f1(f2(f3(f4(x))))



Of course, that's still restricted to precisely four args. Extending

this concept to a variable number of arguments is, uhh, left as an

exercise to the reader. Which will probably end up going back to

reduce(). :)



ChrisA

Chris, call me a snob, but I resent using lambdas (aren't they usually considered odd/bad practice in python?)
 
C

Chris Angelico

Chris, call me a snob, but I resent using lambdas (aren't they usually considered odd/bad practice in python?)

They're not bad practice; all they are is a function without a name,
that's restricted to returning a single expression. So they're
perfectly suited to this task, and ill-suited to some others.

Like everything, lambda's a tool that can be used or misused.

ChrisA
 
F

fp2161

Or have it return the result of f1. And then, since it's an anonymous

function that simply returns an expression, I'd write it as:



def func4(f1,f2,f3,f4):

return lambda x: f1(f2(f3(f4(x))))



Of course, that's still restricted to precisely four args. Extending

this concept to a variable number of arguments is, uhh, left as an

exercise to the reader. Which will probably end up going back to

reduce(). :)



ChrisA

Here is the generalisable version:


def comp(*func):
def anon(x):
res=x
for f in func:
res=f(res)
return res
return anon

you can then compose your function in __main__:
f4=comp(f5,f6,f7)
and call it:
print(x) ...

(I hope that Chris is happy now...)
 
F

fp2161

AdamKal writes:


Hi,

From time to time I have to apply a series of functions to a value
in such a way:



I was wondering if there is a function in standard library that
would take a list of functions and a initial value and do the above
like this:
func_im_looking_for([func1, func2, func3, func4], myval)
I looked in itertools but nothing seamed to do the job. This seams
like something vary obvious that was needed many times elsewhere so
maybe you could help me?



If you can have things backwards, or have func_im_looking_for reverse

things first, you can do it this way:



from functools import reduce



def funcall(arg, fun): return fun(arg)



def func1(arg): return 'f1({})'.format(arg)

def func2(arg): return 'f2({})'.format(arg)

def func3(arg): return 'f3({})'.format(arg)



# prints: f1(f2(f3(3.14)))

print(reduce(funcall, (func3, func2, func1), 3.14))

I had never thought about the "f({0})".format(arg) trick, that is excellent, thank you!!!
 
S

Steven D'Aprano

My way is so obvious that it may not be that interesting...

def func4(f1,f2,f3,f4):
def anon(x):
f1(f2(f3(f4(x))))
return anon

I don't think "obvious" is quite the right description. Well, perhaps
"obviously wrong" :)

You also need to define func1 (trivial), func2, func3, func5, func6,
func7, func8, ..., func2147483647, plus another master function to choose
between them, depending on the number of functions provided as argument.

I assume that the maximum number of arguments given is 2**31-1. Python
may not actually have that limitation, in which case you would need to
define additional functions.

Or... you would have to come up with an implementation which doesn't hard-
code the number of functions used.
 
S

Steven D'Aprano

Chris, call me a snob, but I resent using lambdas (aren't they usually
considered odd/bad practice in python?)

Only among people who dislike functional programming idioms. Like GvR.

It is true that lambda functions are slightly restricted compared to
"normal" functions: they are limited to a single expression, and they all
share the same name '<lambda>', which if you have a lot of them can make
debugging annoying. So *overuse* of lambdas is considered bad form. But
for callbacks and such, they're fine.

It is frowned upon to *directly* bind a lambda to a name, as in this:

plusOne = lambda x: x+1

instead of:

def plusOne(x): return x+1


which is fair enough for production code, but at the interactive
interpreter (and throwaway code) I'll continue to feel free to assign
anonymous functions to names just as I assign anonymous ints and
anonymous lists to names :)

I say "directly" because this of course is allowed:

funcs = [lambda x: x+1, lambda x: x*2, lambda x: x**3]
for func in func:
...


and *much* better than having to predefine plusOne, timesTwo, powerThree
functions.
 
T

Terry Reedy

Here is the generalisable version:

def comp(*func):
def anon(x):
res=x
for f in func:
res=f(res)
return res
return anon

With a bit more work, one can set the __name__ and __qualname__ attributes.

import math as m

def comp(*func, name=''):
def anon(x):
res=x
for f in func:
res=f(res)
return res
if name:
anon.__name__ = name
q = anon.__qualname__.rsplit('.', maxsplit=1)
q[1] = name
anon.__qualname__ = '.'.join(q)
return anon

esincos = comp(m.exp, m.sin, m.cos, name='esincos')
print(esincos)
#
<function comp.<locals>.esincos at 0x00000000033107B8>
 

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

No members online now.

Forum statistics

Threads
473,992
Messages
2,570,220
Members
46,807
Latest member
ryef

Latest Threads

Top