Pythonic累积图

She*_*ohn 5 python python-3.x

是否有更多pythonic方式执行以下操作:

def mysteryFunction( it, fun, val ):
    out = []
    for x in it:
        y,val = fun(x,val)
        out.append(y)
    return out,val
Run Code Online (Sandbox Code Playgroud)

where it是可迭代的,fun是一个接受两个输入并返回两个输出的函数,val是一个初始值,每次调用都会"转换" 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 )
Run Code Online (Sandbox Code Playgroud)

其中dur是持续时间元组,并且rem对应于最终余数(此处为零或小数,具体取决于初始值的类型).这不仅仅是挑选,还有许多其他的例子,例如:整合微分方程的固定步骤方法(可迭代步骤,步进函数,初始状态); 模拟有界随机游走; 深度的树处理没有递归; 等等

Sha*_*ger 6

这种结构类似于itertools.accumulate发电机功能是专为.例如,您的函数可能与这样的函数一起使用:

def add2(x, y):
    return (x + y,) * 2 # Returning the same thing twice, new sum and accumulate sum the same
Run Code Online (Sandbox Code Playgroud)

然后打电话给:

mysteryFunction(range(5), add2, 0)
Run Code Online (Sandbox Code Playgroud)

将返回:

([0, 1, 3, 6, 10], 10)
Run Code Online (Sandbox Code Playgroud)

积累的0到4的总和,以及最后的总和.

itertools.accumulate可以做同样的事情,但它是懒惰的(它按照请求返回每个累计值),并且只适用于两个操作数到单个输出函数; 对于这种情况,它最终变得更简单:

from itertools import accumulate
from operator import add

list(accumulate(range(5), add))
Run Code Online (Sandbox Code Playgroud)

会产生相同listmystery_function(和第二次的结果也只是的最后一个值list),但你也可以懒洋洋地使用没有存储在一个结果list,例如:

for partialsum in accumulate(range(5), add):
    ... do stuff with partialsum ...
Run Code Online (Sandbox Code Playgroud)

您可以按摩accumulate来处理两个输入,两个输出功能(或者更准确地说,从输出的值中丢弃您不关心的值accumulate),但大多数时候我希望第二个输出是累积的迄今为止的价值,并非真正分开,因此避免第二次输出会更清晰.

为了好玩,一种可怕的按摩你的结构匹配accumulate.假设您想为base输入中的每个元素添加一个值,但base每次减少1.有了你的功能,你会做(初base至10):

def addless(new, base):
    return base + new, base - 1

mysteryFunction(range(5), addless, 10)
Run Code Online (Sandbox Code Playgroud)

哪个(感谢传递它range抵消了每个减少base)产生([10, 10, 10, 10, 10], 5).类似的代码accumulate可能是:

def addless2(last_base, new):
    _, base = last_base
    return base + new, base - 1
Run Code Online (Sandbox Code Playgroud)

那么(有些丑陋,因为你不能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)
Run Code Online (Sandbox Code Playgroud)

这让vals作为(10, 10, 10, 10, 10)base作为5,就像在你的代码(道歉魔术; zip广义,嵌套拆包既美丽又可怕的一次全部).