优雅地按顺序应用 Python 函数列表

Apply a list of Python functions in order elegantly

我有一个输入值 val 和一个要按顺序应用的函数列表:

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

如何优雅的申请而不是写作

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

并且也不使用 for 循环:

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

感谢 Martijn 的精彩回答。我发现了一些读物:https://mathieularose.com/function-composition-in-python/ .

使用reduce() function:

# forward-compatible import
from functools import reduce

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

reduce() 将第一个参数(可调用)应用于从第二个参数中获取的每个元素,加上到目前为止的累积结果(如 (result, element))。第三个参数是起始值(否则将使用 funcs 中的第一个元素)。

在Python 3中,内置函数被移动到functools.reduce() location;为了向前兼容,相同的参考在 Python 2.6 及更高版本中可用。

其他语言可能会这样称呼 folding

如果每个函数也需要 中间 结果,请使用 itertools.accumulate()(仅从 Python 3.3 开始,对于采用函数参数的版本) :

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

MartijnPieters 的回答非常好。我唯一要补充的是,这叫做 function composition

为这些泛型命名意味着您可以在需要时使用它们

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

有时你希望它看起来更好 -

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

有时您需要更大的灵活性 -

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