是否有更多的pythonic方式来执行以下操作:
def mysteryFunction( it, fun, val ):
out = []
for x in it:
y,val = fun(x,val)
out.append(y)
return out,val
其中it
是可迭代的,fun
是一个函数,它接受两个输入并返回两个输出,而val
是一个初始值,每次调用{{{ 1}}?
我在问,因为我经常使用fun
和列表理解,但是我无法将之前的功能表达为这些功能的组合,而这已经出现了好几次了。我错过了一个隐藏的习语,或者这只是一个适合自己的利基?
一个具体的例子是从一定的秒数计算(年,周,日,小时,分钟,秒)的持续时间:
map, zip, filter, reduce
其中fac = (365*24*3600, 7*24*3600, 24*3600, 3600, 60, 1)
dur,rem = mysteryFunction( fac, lambda x,y: divmod(y,x), 234567 )
是持续时间元组,dur
对应于最终余数(此处为零或小数,具体取决于初始值的类型)。这不仅仅是挑选,还有许多其他的例子,例如:整合微分方程的固定步骤方法(可迭代步骤,步进函数,初始状态);模拟有界随机游走;深度的树处理没有递归;等
答案 0 :(得分:6)
此结构与the itertools.accumulate
generator function的设计类似。例如,您的函数可能与以下函数一起使用:
def add2(x, y):
return (x + y,) * 2 # Returning the same thing twice, new sum and accumulate sum the same
然后打电话给:
mysteryFunction(range(5), add2, 0)
会返回:
([0, 1, 3, 6, 10], 10)
累计总和0到4,以及最终总和。
itertools.accumulate
可以做同样的事情,但它是懒惰的(它按照请求返回每个累加值),并且只适用于两个操作数到单个输出函数;对于这种情况,它最终变得更简单:
from itertools import accumulate
from operator import add
list(accumulate(range(5), add))
将生成与list
相同的mystery_function
(第二个结果只是list
的最后一个值),但您也可以懒惰地使用它而不将结果存储在a list
,例如:
for partialsum in accumulate(range(5), add):
... do stuff with partialsum ...
您可以按摩accumulate
来处理两个输入,两个输出功能(或者更准确地说,从accumulate
输出的值中丢弃您不关心的值),但大多数情况下我希望第二个输出是迄今为止累积的值,而不是真正分开,所以避免第二个输出会更清晰。
为了好玩,对你的结构进行一种可怕的按摩来匹配accumulate
。假设您想为输入中的每个元素添加base
值,但每次将base
减少1。使用您的功能,您可以(初始base
为10):
def addless(new, base):
return base + new, base - 1
mysteryFunction(range(5), addless, 10)
(感谢传递它range
抵消base
中的每次减少)产生([10, 10, 10, 10, 10], 5)
。与accumulate
类似的代码可能是:
def addless2(last_base, new):
_, base = last_base
return base + new, base - 1
然后(因为你不能直接指定accumulate
的初始值而有些丑陋):
from itertools import accumulate, chain
base = 10
# chain used to provide initial value
accum = accumulate(chain(((None, base),), range(5)), addless2)
next(accum) # Throw away first value that exists solely to make initial tuple
# Put the first value from each `tuple` in `out`, and keep the second value
# only for the last output, to preserve the final base
out, (*_, base) = zip(*accum)
将vals
作为(10, 10, 10, 10, 10)
而base
作为5
,就像在您的代码中一样(为魔术道歉; zip
使用通用的嵌套解包一下子既漂亮又可怕。)