代码之家  ›  专栏  ›  技术社区  ›  iz_

如何有效地在迭代器上迭代“n-wise”

  •  4
  • iz_  · 技术社区  · 6 年前

    可能是复制品,但我找不到任何东西。

    我有一个非常长的迭代器(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 这对我来说并不重要)。我也相信它不会造成很多不必要的切片。这个解决方案可以在不可切片的迭代器上工作,比如文件(我计划使用这些文件)。然而, 迭代工具 解决方案慢了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 方法。有没有更有效的方法来解决这个问题?

    1 回复  |  直到 6 年前
        1
  •  4
  •   Davis Herring    6 年前

    用一个怎么样 deque memoize “你的物品?

    from collections import deque
    
    def nwise_slice(it, n):
        deq = deque((), n)
        for x in it:
            deq.append(x)
            if len(deq)==n: yield deq
    
    my_range = range(8)
    for sub in nwise_slice(my_range, 5):
        print(sub)
    # =>
    # deque([0, 1, 2, 3, 4], maxlen=5)
    # deque([1, 2, 3, 4, 5], maxlen=5)
    # deque([2, 3, 4, 5, 6], maxlen=5)
    # deque([3, 4, 5, 6, 7], maxlen=5)