
生成器中 finally 块的执行时机取决于协程状态而非语句顺序;当迭代器被垃圾回收或显式关闭时,挂起的生成器会强制完成 finally 清理,导致其在主程序逻辑(如 print("Result:", result))之后运行。
生成器中 `finally` 块的执行时机取决于协程状态而非语句顺序;当迭代器被垃圾回收或显式关闭时,挂起的生成器会强制完成 `finally` 清理,导致其在主程序逻辑(如 `print("result:", result)`)之后运行。
在 Python 中,生成器函数的行为与普通函数有本质区别:它不是一次性执行完毕,而是以协程方式按需暂停与恢复。yield 语句会让出控制权并挂起当前执行上下文,包括 try...except...finally 的结构状态。
回顾你的代码:
def tricky_generator(n):
for i in range(n):
try:
if i % 2 == 0:
yield i // 2 # ← 挂起点:函数在此暂停,不继续执行后续语句
else:
raise ValueError(f"Odd value: {i}")
except ValueError as e:
print(e)
yield -1
finally:
print(f"Finally block executed for i={i}") # ← 此处不会立即执行!关键点在于:finally 块仅在对应 try 块的控制流完全退出时才执行。而 yield 并不退出 try 块——它只是暂停执行,保留栈帧和异常处理上下文。因此:
- 当
i = 0, 2, 4(偶数)时,yield i//2执行后函数挂起,finally暂不运行; - 当
i = 1, 3(奇数)时,raise → except → yield -1流程走完,try块因yield结束而“退出”,此时finally立即执行; - 但
i = 4是最后一次yield,之后生成器尚未耗尽(for i in range(5)循环本身还没结束),函数仍处于挂起状态; - 主程序执行完
[next(gen) for _ in range(5)]后,gen对象失去引用,Python 在垃圾回收时调用生成器的__del__方法,触发隐式关闭(GeneratorExit),强制执行剩余finally块 —— 这就是你看到"Finally block executed for i=4"出现在print("Result:", result)之后的原因。
✅ 正确做法:显式关闭生成器,确保清理逻辑及时执行:
gen = tricky_generator(5)
result = [next(gen) for _ in range(5)]
gen.close() # ← 显式关闭,触发最后的 finally
print("Result:", result)⚠️ 注意事项:
- 不要依赖垃圾回收时机执行关键清理(如释放文件句柄、数据库连接);
- 若需确定性资源管理,请使用
contextlib.closing()或with contextlib.closing(tricky_generator(5)) as gen:; -
finally在生成器中主要用于“无论是否 yield 都需执行的收尾”,但其实际执行点可能延迟到生成器生命周期终结。
总结:生成器的 finally 不是“语句级同步执行”,而是“结构级终结保障”。理解其与协程生命周期的绑定,是写出健壮生成器代码的关键。


















