Apply Python function list to elegant

I have an input val and a list of functions that will be applied in the following order:

 funcs = [f1, f2, f3, ..., fn] 

How to apply elegantly and not write

 fn( ... (f3(f2(f1(val))) ... ) 

and also not used for loop:

 tmp = val for f in funcs: tmp = f(tmp) 

Thanks Martijn for the excellent answer. There I found some readings: https://mathieularose.com/function-composition-in-python/ .

+9
python functional-programming
source share
2 answers

Use reduce() function :

 # forward-compatible import from functools import reduce result = reduce(lambda res, f: f(res), funcs, val) 

reduce() applies the first argument called to each element taken from the second argument, plus the accumulated result so far (like (result, element) ). The third argument is the initial value (the first element from funcs will be used differently).

In Python 3, the inline function was moved to functools.reduce() location ; for direct compatibility, the same link is available in Python 2.6 and later.

Other languages ​​may call it folding .

If you need intermediate results for each function, use itertools.accumulate() (only with Python 3.3 for the version that takes a function argument):

 from itertools import accumulate, chain running_results = accumulate(chain(val, funcs), lambda res, f: f(res)) 
+18
source share

MartijnPieters answer is excellent. The only thing I would like to add is that this is called a composition of functions.

Naming these generics means you can use them if necessary

 from functools import reduce def id(x): return x def comp(f,g): return lambda x: f(g(x)) def compose(*fs): return reduce(comp, fs, id) # usage # compose(f1, f2, f3, ..., fn) (val) print(compose (lambda x: x + 1, lambda x: x * 3, lambda x: x - 1) (10)) # = ((10 - 1) * 3) + 1 # = 28 

Sometimes you want it to look better though -

 def seq (x): return lambda k: seq (k (x)) def fn1 (x): return x - 1 def fn2 (x): return x * 3 def fn3 (x): return x + 1 seq (10) (fn1) (fn2) (fn3) (print) # 28 # 10 9 27 28 

And sometimes you want more flexibility -

 from operator import add, mul, sub def seq (x): return lambda k, *v: seq (k (x, *v)) seq (10) (sub, 1) (mul, 3) (add, 1) (print) # 28 # 10 9 27 28 seq (1) (add, 2) (add, 3) (add, 4) (add, 5) (print) # 15 # 1 3 6 10 15 sum = seq(0) for x in range(10): sum = sum (add, x) sum (print) # 45 
+3
source share

All Articles