有没有一种Python风格的方法可以链接这些函数调用?

4

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

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的方式?还是现在这样就可以了?


12
我认为你的那部分内容看起来非常完美。很清楚正在发生什么。有时在追求使代码“更具Python特色”时,实际上只会让它变得更加难以阅读。 - Jonathon Reinhart
只是挑剔一下,但是 for val, fun in reversed(zip(vals[:-1], ops)): 应该改为 for val, fun in reversed(zip(vals[:-1], funs)): - zehnpaard
嗯,看起来你需要一个与“reduce”相对应的函数,它也可以改变函数,很有趣。 - Tadhg McDonald-Jensen
2个回答

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

我仍然更喜欢你现有的基本循环,但这是一种替代方案,我认为值得提供。


2
您提供的解决方案在我看来很符合Python的风格。我不确定为什么您认为它不是Pythonic的。评论中有人提到了reduce()函数,但Guido van Rossum认为reduce()不符合Python的风格,我认为当可以使用迭代解决方案时,变体也可能不符合Python的风格。值得一提的是,您也可以使用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)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接