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
for el in chunk:
File "xxxx.py", line 2, in head
for cnt, el in enumerate(iterable):
KeyboardInterrupt
Нуууу... оно никогда не останавливается (мне приходится нажимать ^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
for el in chunk:
File "xxxx.py", line 9, in head
if cnt == 0 : raise NoMoreData
__main__.NoMoreData()
Как определить, что генератор исчерпан в функции chunks, не проходя его?
(Этот вопрос связан с этим и этим, но это предварительный обход генератора, а это именно то, чего я хочу избежать)
Я хотел бы разделить генератор на куски. Требования следующие:
[list] [*]не дополнять фрагменты: если количество оставшихся элементов меньше размера фрагмента, последний фрагмент должен быть меньше. [*]не ходите по генератору заранее: вычисление элементов требует больших затрат, и это должно выполняться только потребляющей функцией, а не чанкером [*]что означает, конечно: не накапливать в памяти (нет списков) [/list]
Я попробовал следующий код:
[code]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) [/code]
И это как-то работает:
[code]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 for el in chunk: File "xxxx.py", line 2, in head for cnt, el in enumerate(iterable): KeyboardInterrupt [/code]
Нуууу... оно никогда не останавливается (мне приходится нажимать ^C) из-за while True. Я хотел бы остановить этот цикл всякий раз, когда генератор потребляется, но я не знаю, как обнаружить эту ситуацию. Я попытался создать исключение:
[code]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) [/code]
Но тогда исключение возникает только в контексте потребителя, а это не то, чего я хочу (я хочу сохранить код потребителя в чистоте)
[code]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 for el in chunk: File "xxxx.py", line 9, in head if cnt == 0 : raise NoMoreData __main__.NoMoreData() [/code]
Как определить, что генератор исчерпан в функции chunks, не проходя его?