尝试将大小为 a*n + b 的生成器拆分为 1 个生成器,每个生成器的大小为 n。
我相信这个问题已经存在问题,只需进行最小的修改这里。然而令人困惑的是,下面的代码返回一个错误,尽管据说已经用 try except 捕获了它。
def test(vid, size):
while True:
try:
part = (next(vid) for _ in range(size))
yield part
except StopIteration:
break
res = test((i for i in range(100)), 30)
for i in res:
for j in i:
print(i, end=" ")
print()
---------------------------------------------------------------------------
StopIteration Traceback (most recent call last)
Cell In[54], line 4, in (.0)
3 try:
----> 4 part = (next(vid) for _ in range(size))
5 yield part
StopIteration:
The above exception was the direct cause of the following exception:
RuntimeError Traceback (most recent call last)
Cell In[54], line 11
9 res = test((i for i in range(100)), 30)
10 for i in res:
---> 11 for j in i:
12 print(i, end=" ")
13 print()
RuntimeError: generator raised StopIteration
此外,打印语句中的剩余部分也被打印出来,而不是被丢弃。
注意:使用
RuntimeError
表示 except 也不会捕获错误
有几件事需要解决:
try...except
一定是在迭代发生的地方,目前它在定义周围。让我给你这个类比,以使其(希望如此)显而易见:
foo
不会在test2
函数内部被调用 -> 因此无法在内部捕获错误。
def test2():
try:
def foo():
raise StopIteration
return foo
except StopIteration #this will not happen as foo() is not called here.
pass
outer_foo = test2()
outer_foo() # <--- raises StopIteration
test
。正如你所说,发生了
RuntimeError
。
try:
for geni in test():
for elem in geni:
print(elem)
except StopIteration:
print("This is expected but does NOT HAPPEN. Why?")
except RuntimeError:
print("Instead a RuntimeError occurs.")
那么,为什么这段代码没有捕获 StopIteration
而是产生
RuntimeError
?原因在于你如何定义生成器以及生成器在 python 中如何工作。生成器定义为:
part = (next(vid) for _ in range(size)) # <--- StopIteration CANNOT be caught
Python 中的生成器有自己的作用域 来自 StopIteration
的预期
next(vid)
在该作用域中引发但未在生成器的作用域中处理,它将 作为
RuntimeError
传播到外部 。你的发电机相当于。
for _ in range(size):
yield next(vid) # <--- here a StopIteration can be caught
此版本没有“隐藏”范围,并且行为符合预期,您只需要注意,如果发生 vid
的迭代,则必须再次捕获错误。这是一个可行的解决方案:
def create_batches(vid, size):
done = False
def batcher():
nonlocal done
print("--- new batch ---")
for i in range(size):
print("batch", i, "/", size)
try:
yield next(vid)
except StopIteration:
print("StopIteration caught, and we are done")
done=True
break
while not done:
yield batcher()
for batch in create_batches((i for i in range(10)), 3):
for elem in batch:
print("elem=", elem)
# --- Output --- #
--- new batch ---
batch 0 / 3
elem= 0
batch 1 / 3
elem= 1
batch 2 / 3
elem= 2
--- new batch ---
batch 0 / 3
elem= 3
batch 1 / 3
elem= 4
batch 2 / 3
elem= 5
--- new batch ---
batch 0 / 3
elem= 6
batch 1 / 3
elem= 7
batch 2 / 3
elem= 8
--- new batch ---
batch 0 / 3
elem= 9
batch 1 / 3
StopIteration caught, and we are done
itertools.isclice
创建更紧凑的批处理程序,但为了更好地理解,我没有将其包含在此处。