
Python 生成器在被垃圾回收或显式关闭时会触发未完成的 finally 块,即使所有 yield 已返回完毕;这是因为生成器函数实际处于暂停(suspended)状态,而非正常终止。
python 生成器在被垃圾回收或显式关闭时会触发未完成的 `finally` 块,即使所有 `yield` 已返回完毕;这是因为生成器函数实际处于暂停(suspended)状态,而非正常终止。
在你的代码中,tricky_generator(5) 创建了一个生成器对象,它将依次处理 i = 0, 1, 2, 3, 4。前四次调用 next(gen) 分别对应 i=0 到 i=3,而第五次调用 next(gen) 对应 i=4 —— 此时满足 i % 2 == 0,执行 yield i // 2(即 yield 2),随后函数暂停在该 yield 表达式处,并未继续执行后续语句(包括 finally 块)。
关键点在于:finally 块属于 try...except...finally 语句的组成部分,它在该 try 块退出时(无论正常结束、异常退出,还是被 yield 暂停后最终清理)都会执行。
但注意:yield 并不会立即触发 finally;它只是暂停执行。真正的 finally 触发时机是——当生成器对象被销毁(例如作用域结束、显式调用 gen.close() 或被 GC 回收)时,Python 会强制“清理”挂起的生成器帧,此时才执行尚未运行的 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}")
gen = tricky_generator(5)
result = [next(gen) for _ in range(5)] # 拉取全部 5 个值 → i=0~4 各执行一次 try 块
print("Result:", result)
# 此时 gen 仍处于活跃但已耗尽状态(再调 next(gen) 会抛 StopIteration)
# 但在 Python 解释器退出前,gen 被自动清理 → 触发 i=4 的 finally!输出如下:
Finally block executed for i=0 Finally block executed for i=1 Finally block executed for i=2 Finally block executed for i=3 Result: [0, -1, 1, -1, 2] Finally block executed for i=4 ← 这行出现在 "Result:" 之后!
✅ 原因总结:
-
i=0~3的finally在每次next()返回前就已执行(因为对应try块因yield或except完成而退出); -
i=4的try块在yield 2后暂停,未退出,因此finally被延迟; - 当
gen对象离开作用域且被销毁时(通常发生在脚本末尾 GC 阶段),Python 强制完成该try块的退出流程 → 执行finally。
⚠️ 注意事项:
- 不要依赖
finally在生成器中做“确定性资源释放”,因为它可能延迟到 GC 时刻(不可预测); - 若需及时清理,请显式调用
gen.close()(它会引发GeneratorExit,触发finally立即执行); -
gen.close()是安全的:对已耗尽或已关闭的生成器重复调用无副作用。
✅ 推荐改进写法(确保及时清理):
gen = tricky_generator(5)
try:
result = [next(gen) for _ in range(5)]
print("Result:", result)
finally:
gen.close() # 显式触发 finally,避免依赖 GC这样,“Finally block executed for i=4” 就会稳定出现在 print("Result:", result) 之前,行为完全可控。

















