是否存在链接这些函数调用的pythonic方法?

时间:2015-01-05 03:27:55

标签: python

我有一些代码,其中我最终得到了二进制函数列表和值列表,我需要像这样链接调用:

funs = [..]
vals = [..]

result = funs[0](vals[0],
           funs[1](vals[1],
               ..
               funs[-1](vals[-2], vals[-1])))))..)

举一个简单的例子,如果:

funs = [operator.add, operator.mul]
vals = [1, 2, 3]

然后result应该最终评估add(1, mul(2, 3)),结果为7.我可以编写一个for循环来评估每个中间结果,这同时也很容易且令人难以置信地不满意:

result = vals[-1]
for val, fun in reversed(zip(vals[:-1], funs)):
    result = fun(val, result)

Pythonic的方式是什么?或者这样就好了吗?

2 个答案:

答案 0 :(得分:2)

这似乎与reduce非常相似,但每次使用的函数都不同,因此一种解决方案是创建一个每次调用时都使用不同函数的对象:

class Cycled_Functions:
    def __init__(self,functions):
        self.functions = iter(functions)
    def __call__(self,*args,**kw):
        f = next(self.functions) #will raise StopIteration at some point
        return f(*args,**kw)

然后Cycled_Functions(reversed(FUNCTIONS))适合与reduce一起使用来完成您的任务:

import operator
from functools import reduce

funs = [operator.add, operator.mul]
vals = [1, 2, 3]

f = Cycled_Functions(reversed(funs))

print(reduce(f, reversed(vals)))

我仍然更喜欢你拥有的基本循环,但这是另一种选择,我觉得它值得提供。

答案 1 :(得分:1)

您提供的解决方案对我来说似乎是Pythonic。我不确定你为什么认为不是。人们在评论中提到了reduce(),但reduce() is considered non-Pythonic by Guido van Rossum并且我认为当迭代解决方案成为可能时,它的变化也是如此。对于它的价值,您可以使用reduce()来编写它,但是:

def foo(funs, vals):
    """
    >>> import operator
    >>> funs = [operator.add, operator.mul]
    >>> vals = [1, 2, 3]
    >>> foo(funs, vals)
    7
    """
    return reduce(lambda z, (fun, val): fun(val, z), reversed(zip(funs, vals[:-1])), vals[-1])

为了说明,您可以将其写为递归函数,但递归比迭代慢,并且受到调用堆栈大小限制的限制。

def foo(funs, vals):
    """
    >>> import operator
    >>> funs = [operator.add, operator.mul]
    >>> vals = [1, 2, 3]
    >>> foo(funs, vals)
    7
    """
    if len(funs) > 1:
        fun, val = funs[0], vals[0]
        return fun(val, foo(funs[1:], vals[1:]))
    else:
        [fun] = funs
        return fun(*vals)