【发布时间】:2019-06-14 06:26:11
【问题描述】:
可能是重复的,但我找不到任何东西。
我有一个很长的迭代器(10000 个项目),我需要一次迭代大约 500 个项目。所以如果我的迭代器是range(10000),它看起来像这样:
Iteration #1: 0, 1, 2, ... 497, 498, 499
Iteration #2: 1, 2, 3, ... 498, 499, 500
Iteration #3: 2, 3, 4, ... 499, 500, 501
Iteration #4: 3, 4, 5, ... 500, 501, 502
...
Iteration #9500: 9499, 9500, 9501 ... 9996, 9997, 9998
Iteration #9501: 9500, 9501, 9502 ... 9997, 9998, 9999
等等。有这个方法:
def nwise_slice(lst, n):
for i in range(len(lst) - n + 1):
yield lst[i:i + n]
但是,这不适用于惰性迭代器。我尝试使用迭代器创建一个解决方案,并改编自 itertools pairwise 和 consume 配方(参见 here)来创建这个:
import itertools
def nwise_iter(lst, n):
iters = itertools.tee(lst, n)
for idx, itr in enumerate(iters):
next(itertools.islice(itr, idx, idx), None)
for group in zip(*iters):
yield group
也是一样的(尽管会产生 tuple 而不是 list,这对我来说并不重要)。我也相信它不会产生很多不必要的切片。该解决方案适用于不可切片的迭代器,例如文件(我计划使用)。但是,itertools 解决方案的速度要慢 2 倍:
In [4]: %timeit list(nwise_slice(list(range(10000)), 500))
46.9 ms ± 729 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
In [5]: %timeit list(nwise_iter(list(range(10000)), 500))
102 ms ± 3.95 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
我不想为了利用slice 方法而将所有测试数据加载到内存中。有没有更有效的方法来解决这个问题?
【问题讨论】:
-
查看我的answer 中的
grouper()函数。 -
@martineau 谢谢。不幸的是,这会迭代
(1, 2, 3), (4, 5, 6)而不是(1, 2, 3), (2, 3, 4), (3, 4, 5), (4, 5, 6) -
您是否需要迭代器的结果在迭代推进时保持有效?您是否需要通过索引对每个子序列进行 O(1) 访问?
-
@DavisHerring 不,所有这些都是不需要的。我有能力“搞砸”原来的迭代器。
-
可以使用滑动窗口算法。不是更快,而是更简洁,看
more_itertoolslibrarylist(more_itertools.windowed(range(10000), 500))。
标签: python python-3.x list iterator itertools