Возможно, дубликат, но я ничего не смог найти.
У меня очень длинный итератор (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
(см. здесь ), чтобы создать это:
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
. Есть ли более эффективный способ осуществить это?