Если у вас есть список в Python 3.7:
>>> li
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
Вы можете превратить это в список кусков каждой длины n
с одной из двух общих идиом Python:
>>> n=3
>>> list(zip(*[iter(li)]*n))
[(0, 1, 2), (3, 4, 5), (6, 7, 8)]
Который капли последнего неполного кортежа, так как (9,10)
не длина n
Вы также можете сделать:
>>> [li[i:i+n] for i in range(0,len(li),n)]
[[0, 1, 2], [3, 4, 5], [6, 7, 8], [9, 10]]
если вы хотите получить последний дополнительный список, даже если он содержит менее n
элементов.
Предположим теперь, что у меня есть генератор, gen
, неизвестная длина или прекращение (так что list(gen))
вызовов list(gen))
или sum(1 for _ in gen)
не будет разумным), где я хочу каждый кусок.
Лучшее выражение генератора, которое я смог придумать, - это что-то в этом роде:
from itertools import zip_longest
sentinel=object() # for use in filtering out ending chunks
gen=(e for e in range(22)) # fill in for the actual gen
g3=(t if sentinel not in t else tuple(filter(lambda x: x != sentinel, t)) for t in zip_longest(*[iter(gen)]*n,fillvalue=sentinel))
Это работает по назначению:
>>> next(g3)
(0, 1, 2)
>>> next(g3)
(3, 4, 5)
>>> list(g3)
[(6, 7, 8), (9, 10)]
Это просто кажется - неуклюжий. Я старался:
- с использованием
islice
но отсутствие длины кажется трудно преодолеть; - используя дозорный
iter
вiter
но в дозорной версииiter
требуется вызываемый, а не итерируемый.
Есть ли более идиоматический метод Python 3 для генератора кусков длины n
включая последний патрон, который может быть меньше n
?
Я также открыт для функции генератора. Я ищу что-то идиоматическое и в основном более читаемое.
Обновить:
Метод DSM в его удаленном ответе очень хорош, я думаю:
>>> g3=(iter(lambda it=iter(gen): tuple(islice(it, n)), ()))
>>> next(g3)
(0, 1, 2)
>>> list(g3)
[(3, 4, 5), (6, 7, 8), (9, 10)]
Я открыт для того, чтобы этот вопрос был дублированным, но связанный вопрос почти 10 лет и сосредоточен на списке. Нет нового метода в Python 3 с генераторами, где вы не знаете длину и не хотите больше, чем кусок за раз?