关于Python 3生成器try-finally执行异常及for与next差异的问询
生成器中finally执行时机与next/for循环的差异解析
咱们先拆解第一个问题:为什么运行print(next(gen()))会出现stop和1的输出(注:实际运行顺序通常是先输出1再输出stop,可能是你描述时的顺序偏差,但核心是finally块被执行了)?
先看你的代码:
def gen(): try: while True: yield 1 finally: print("stop") print(next(gen()))
这里的关键细节是:你调用gen()时创建了一个临时生成器对象,但没有把它赋值给任何变量。当next()调用这个生成器时,生成器启动执行,走到yield 1时暂停,把1返回给print()输出。但此时这个临时生成器对象已经没有任何引用了——没有变量持有它,Python的垃圾回收机制会立刻销毁这个对象。
底层机制:生成器的关闭与finally执行
生成器是特殊的迭代器,它的生命周期里有个关键的close()方法:当生成器对象被垃圾回收,或者手动调用close()时,Python会在生成器当前暂停的位置抛出GeneratorExit异常。这个异常会触发生成器继续执行,直到碰到finally块并执行其中的代码,然后生成器正式退出。
在这个场景里,临时生成器被回收时触发了close(),于是生成器从yield 1的暂停点继续走,进入finally块执行print("stop"),这就是为什么你会看到stop的输出。
for循环与next()的核心差异
那为什么for i in gen(): print(i)会进入无限循环,而不会触发finally呢?
这本质是生成器对象的引用持有问题:
- 对于
next(gen()):生成器是临时创建的,用完就丢,没有任何变量引用它,所以立刻被回收,触发close()和finally。 - 对于
for循环:for语句会持续持有生成器对象的引用,不断调用next()获取值。你的生成器是无限yield 1,永远不会抛出StopIteration(迭代结束的标志),所以生成器一直处于“挂起-被唤醒-挂起”的循环中,永远不会被垃圾回收,finally块自然也就不会执行——除非你手动中断程序(比如Ctrl+C),此时Python会触发生成器的close(),才会执行finally里的代码。
内容的提问来源于stack exchange,提问作者ruanhao
相关产品推荐
相关产品推荐

