(Этот вопрос связан с этим и этим, но это предварительные шаги генератор, которого я хочу избежать)
Я хотел бы разделить генератор на куски. Требования:
- не вставляйте куски: если количество оставшихся элементов меньше размера блока, последний фрагмент должен быть меньше.
- не прогоните генератор заранее: вычисление элементов дорого, и это должно выполняться только функцией потребления, а не с помощью chunker
- что означает, конечно: не накапливать в памяти (без списков)
Я пробовал следующий код:
def head(iterable, max=10):
for cnt, el in enumerate(iterable):
yield el
if cnt >= max:
break
def chunks(iterable, size=10):
i = iter(iterable)
while True:
yield head(i, size)
# Sample generator: the real data is much more complex, and expensive to compute
els = xrange(7)
for n, chunk in enumerate(chunks(els, 3)):
for el in chunk:
print 'Chunk %3d, value %d' % (n, el)
И это как-то работает:
Chunk 0, value 0
Chunk 0, value 1
Chunk 0, value 2
Chunk 1, value 3
Chunk 1, value 4
Chunk 1, value 5
Chunk 2, value 6
^CTraceback (most recent call last):
File "xxxx.py", line 15, in <module>
for el in chunk:
File "xxxx.py", line 2, in head
for cnt, el in enumerate(iterable):
KeyboardInterrupt
Buuuut... он никогда не останавливается (я должен нажать ^C
) из-за while True
. Я хотел бы остановить этот цикл всякий раз, когда генератор был израсходован, но я не знаю, как обнаружить эту ситуацию. Я попытался создать исключение:
class NoMoreData(Exception):
pass
def head(iterable, max=10):
for cnt, el in enumerate(iterable):
yield el
if cnt >= max:
break
if cnt == 0 : raise NoMoreData()
def chunks(iterable, size=10):
i = iter(iterable)
while True:
try:
yield head(i, size)
except NoMoreData:
break
# Sample generator: the real data is much more complex, and expensive to compute
els = xrange(7)
for n, chunk in enumerate(chunks(els, 2)):
for el in chunk:
print 'Chunk %3d, value %d' % (n, el)
Но тогда исключение возникает только в контексте потребителя, чего я не хочу (я хочу, чтобы потребительский код был чистым)
Chunk 0, value 0
Chunk 0, value 1
Chunk 0, value 2
Chunk 1, value 3
Chunk 1, value 4
Chunk 1, value 5
Chunk 2, value 6
Traceback (most recent call last):
File "xxxx.py", line 22, in <module>
for el in chunk:
File "xxxx.py", line 9, in head
if cnt == 0 : raise NoMoreData
__main__.NoMoreData()
Как я могу обнаружить, что генератор исчерпан в функции chunks
, не пройдя его?