为什么当迭代它的循环被中断时,生成器会引发异常?

ale*_*123 -1 python exception generator

def gen():
    try:
        yield 1
        yield 2
    except:
        print('hi')
def func():
    for x in gen():
        return x
print(func())
Run Code Online (Sandbox Code Playgroud)

此代码打印hi然后打印1。为什么它不直接打印1?提出了什么例外?

Jor*_*uis 6

func从生成器中检索第一个元素gen。之后,func有一个return声明。结果,func执行终止,导致gen生成器也退出。close当调用生成器的方法时,它会引发一个GeneratorExit异常,这就是本例中捕获的异常。这就是它打印的原因hi。之后,程序继续;func返回1并且该值被打印在代码的最后一行中。

generator.close方法被调用是因为生成器死亡。循环之后for,不再有对生成器对象的剩余引用。如果将生成器分配给变量a = gen()并且没有完全消耗其迭代,则可以GeneratorExit通过直接调用a.close()或使用 终止对象来触发 的引发del a

另一方面,如果您允许生成器完成其执行,它将不会引发GeneratorExit。当循环for _ in gen(): pass完成时,它成功结束生成器的执行gen,从而产生StopIteration由循环处理的异常for。但它不会提高GeneratorExit,因为在close调用时,执行gen已经结束。


有趣的是,它GeneratorExit不是继承自Exception而是继承自BaseException。如果你写except Exception: print('hi'),只会1被打印。这样,您可以管理生成器中引发的任何异常,而无需中断其执行:

def gen():
    for i in (1, 2):
        try:
            yield i
        except Exception:
            pass
Run Code Online (Sandbox Code Playgroud)

如果您要使用except: pass它,它将捕获GeneratorExit并允许执行gen继续产生另一个值。根据generator.close文档,这不应该发生:

如果生成器产生一个值,则 aRuntimeError被提升。

事实上,你会遇到RuntimeError:生成器忽略 GeneratorExit