【发布时间】:2016-04-28 06:37:57
【问题描述】:
(这个问题与this one和this one有关,但这些都是预先遍历生成器,这正是我想要避免的)
我想将生成器拆分成块。要求是:
- 不要填充块:如果剩余元素的数量小于块大小,则最后一个块必须更小。
- 不要事先遍历生成器:计算元素的成本很高,而且只能由消费函数完成,不能由分块器完成
- 这当然意味着:不要在内存中累积(没有列表)
我已经尝试了以下代码:
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 ...因为while True,它永远不会停止(我必须按^C)。每当生成器被消耗时,我想停止该循环,但我不知道如何检测这种情况。我已经尝试提出异常:
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 函数中检测到生成器已耗尽,而无需遍历它?
【问题讨论】:
-
不知道如何解决它,但
except只会在 创建head时引发异常,而不是在迭代它时捕获异常。 -
@tobias_k:当然,我明白这一点。我正在寻找解决方案...
-
可以偷看第一个元素吗?您可以尝试
next第一个元素,然后引发异常或返回实际的块迭代器。 -
@tobias_k:这将是一个很好的折衷方案,但不确定如何在不丢失该元素的情况下实现它...
-
你能解释一下“预先运行发电机”是什么意思吗?