Решения, использующие диапазоны с шагами, работают только с последовательностями, такими как списки и кортежи (не итераторы). Они также не так эффективны, как могли бы, поскольку они обращаются к последовательности много раз, вместо того, чтобы повторять ее один раз.
Вот версия, которая поддерживает итераторы и выполняет итерации только один раз, создавая список списков:
def blockify(iterator, blocksize):
"""Split the items in the given iterator into blocksize-sized lists.
If the number of items in the iterator doesn't divide by blocksize,
a smaller block containing the remaining items is added to the result.
"""
blocks = []
for index, item in enumerate(iterator):
if index % blocksize == 0:
block = []
blocks.append(block)
block.append(item)
return blocks
И теперь версия итератора, которая возвращает итератор кортежей, не имеет дополнительной памяти и позволяет выбирать, включать ли остаток. Обратите внимание, что вывод может быть преобразован в список через список (blockify (...)).
from itertools import islice
def blockify(iterator, blocksize, include_remainder=True):
"""Split the items in the given iterator into blocksize-sized tuples.
If the number of items in the iterator doesn't divide by blocksize and
include_remainder is True, a smaller block containing the remaining items
is added to the result; if include_remainder is False the remaining items
are discarded.
"""
iterator = iter(iterator) # we need an actual iterator
while True:
block = tuple(islice(iterator, blocksize))
if len(block) < blocksize:
if len(block) > 0 and include_remainder:
yield block
break
yield block