Apply a list of Python functions in order elegantly

戏子无情 提交于 2019-12-31 22:26:12

问题


I have an input value val and a list of functions to be applied in the order:

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

How to apply elegantly and not writing

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

and also not using for loop:

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

Thanks Martijn for the awesome answer. There's some reading I found: https://mathieularose.com/function-composition-in-python/ .


回答1:


Use the reduce() function:

# forward-compatible import
from functools import reduce

result = reduce(lambda res, f: f(res), funcs, val)

reduce() applies the first argument, a callable, to each element taken from the second argument, plus the accumulated result so far (as (result, element)). The third argument is a starting value (the first element from funcs would be used otherwise).

In Python 3, the built-in function was moved to the functools.reduce() location; for forward compatibility that same reference is available in Python 2.6 and up.

Other languages may call this folding.

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

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



回答2:


MartijnPieters answer is excellent. The only thing I would add is that this is called function composition

Giving names to these generics means you can use them whenever the need arises

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 nicer 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


来源:https://stackoverflow.com/questions/39123375/apply-a-list-of-python-functions-in-order-elegantly

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!