python 的生成器函数中引发的 StopIteration 异常处理

StopIteration exception handling raised in generator function in python

这是我的生成器函数代码:

def fibo():
    n1=0
    n2=1
    while True:
        if n1 == 8:
            raise StopIteration
        else:
            yield n1
            n1,n2=n2,n1+n2
        
seq=fibo()

这是我用于生成序列的代码版本 1:

for ele in seq:
    print(next(seq))

输出:

1
2
5

RuntimeError: generator raised StopIteration
---------------------------------------------------------------------------
StopIteration                             Traceback (most recent call last)
<ipython-input-3-c01815b93e23> in fibo()
      5         if n1 == 8:
----> 6             raise StopIteration
      7         else:

StopIteration: 

The above exception was the direct cause of the following exception:

RuntimeError                              Traceback (most recent call last)
<ipython-input-3-c01815b93e23> in <module>
     11 seq=fibo()
     12 
---> 13 for ele in seq:
     14     print(next(seq))
     15 

RuntimeError: generator raised StopIteration

版本 1 的预期输出:

0
1
1
2
3
5

这是我用于生成序列的代码版本 2:

while True:
    try:
        print(next(seq))
    except StopIteration:
        print('This exception is raised in the generator operation defining function. Hence it is handled here.')
        break

输出:

0
1
1
2
3
5
RuntimeError: generator raised StopIteration
---------------------------------------------------------------------------
StopIteration                             Traceback (most recent call last)
<ipython-input-4-6afe26583a33> in fibo()
      5         if n1 == 8:
----> 6             raise StopIteration
      7         else:

StopIteration: 

The above exception was the direct cause of the following exception:

RuntimeError                              Traceback (most recent call last)
<ipython-input-4-6afe26583a33> in <module>
     16 while True:
     17     try:
---> 18         print(next(seq))
     19     except StopIteration:
     20         print('This exception is raised in the generator operation defining function. Hence it is handled here.')

RuntimeError: generator raised StopIteration

版本 2 的预期输出:

0
1
1
2
3
5
This exception is raised in the generator operation defining function. Hence it is handled here.

第一个版本out不正确,出现异常。在第二个中,除了正确的输出之外,还发生了与版本 1 相同的异常。 请帮我纠正一下。

不用引发异常,只需使用 return 即可

if n1 == 8:
    return

当您的生成器产生 StopIteration 错误时,实际程序收到 RuntimeError。以下将起作用:

seq=fibo()
while True:
    try:
        print(next(seq))
    except RuntimeError:
        print('This exception is raised in the generator operation defining function. Hence it is handled here.')
        break

Here is my code version-1 for generating sequence:

Python 中的 for 循环构造的要点是 已经 调用 next 在迭代器上(它从生成器自动创建)。所以你应该 print(ele) 代替。

RuntimeError: generator raised StopIteration

是的,因为您不应该在生成器中使用显式 raise StopIteration。这是一个 old 工具,用于向 for 循环传达您没有元素的信息,仅当您实现 __iter__ 老式方法时才需要方法。是的,for 循环构造依赖于这个异常来确定输入的结束;但是从您的生成器创建的迭代器将自动执行此操作 - 并且还会检查生成器中引发的 StopIterations 并将它们转换为 RuntimeError,明确地 prevent 您的StopIteration 摆脱循环。

如果底层机器没有那个检查,那么你可以做类似 for i in itertools.chain(my_generator, a_list): 的事情,并且生成器实现引发的异常会 强行中断 循环(毕竟,循环通过 处理该异常 退出)并防止 a_list 元素被看到。这不是您希望生成器的行为方式,并且在某种意义上它也是一种安全风险,因此存在检查。

如果你想发信号通知生成器已完成 yielding,你要做的是......到达代码的末尾。例如,使用 return,或者在这种情况下 break 也有效(因为循环后没有其他内容):

def fibo():
    n1, n2 = 0, 1
    while True:
        if n1 == 8:
            break
        else:
            yield n1
            n1, n2 = n2, n1+n2

print('This exception is raised in the generator operation defining function. Hence it is handled here.')

没有发生的原因是您正在尝试捕获 StopIteration(您 不应该这样做 因为它会干扰循环的操作!) ,但例外是(如果您仔细阅读堆栈跟踪,您会知道)替换为 RuntimeError 。见上文。

使用上面更正的代码,一切都按预期工作:

>>> list(fibo())
[0, 1, 1, 2, 3, 5]

>>> x = fibo()
>>> for f in x:
...   print(f)
...
0
1
1
2
3
5

>>> x = fibo()
>>> while True:
...   try:
...     print(next(x))
...   except StopIteration:
...     print('this works as intended - but a for loop is much cleaner')
...     break
...
0
1
1
2
3
5
this works as intended - but a for loop is much cleaner