首页 > 应用开发 > Python > Python 生成器在异常传播中的边缘行为
1️⃣ 基础:生成器的异常传播
在 Python 中,生成器是一种 可迭代对象,可以暂停和恢复执行:
def gen():
yield 1
yield 2
当异常在生成器内部发生时,如果没有捕获,会 传播到外部迭代者:
def gen():
yield 1
raise ValueError("oops")
yield 2
g = gen()
print(next(g)) # 1
print(next(g)) # ValueError: oops
✅ 基本行为直观:生成器内部异常会像普通函数一样传播。
2️⃣ 生成器的 throw() 方法
生成器对象有一个 throw() 方法,可以向生成器内部“注入”异常:
def gen():
try:
yield 1
except KeyError:
print("Caught KeyError")
yield 2
g = gen()
print(next(g)) # 1
g.throw(KeyError) # Caught KeyError
print(next(g)) # 2
微妙点:
throw() 会在当前暂停的 yield 处抛出异常。
如果生成器捕获了异常,它会继续执行下一条 yield。
如果没有捕获,异常会传播到外部,生成器终止。
⚠️ 注意:如果生成器在 finally 里有清理代码,异常传播顺序可能影响清理行为。
3️⃣ close() 方法与 GeneratorExit
生成器对象的 close() 会向生成器内部 抛入一个 GeneratorExit 异常:
def gen():
try:
yield 1
finally:
print("Cleaning up")
g = gen()
next(g) # 1
g.close() # Cleaning up
边缘行为:
如果生成器捕获了 GeneratorExit 并 没有重新抛出,Python 会 报 RuntimeError,因为 GeneratorExit 必须传播:
def gen():
try:
yield 1
except GeneratorExit:
print("Caught GeneratorExit") # ⚠️
# not re-raising
g = gen()
next(g)
g.close() # RuntimeError: generator ignored GeneratorExit
在 finally 中仍然可以清理资源,但要小心不要吞掉 GeneratorExit。
4️⃣ 异常与生成器状态的交互
生成器有三种关键状态:
GEN_CREATED:未开始迭代
GEN_RUNNING:正在执行
GEN_SUSPENDED:在 yield 处暂停
GEN_CLOSED:迭代结束或异常退出
异常传播与状态紧密相关:
next() 过程中异常 → 生成器关闭(状态变为 GEN_CLOSED)
throw() 注入异常 → 如果生成器捕获,状态仍是 SUSPENDED;否则关闭
close() → 注入 GeneratorExit → 状态关闭
边缘案例:
def g():
yield 1
yield 2
gen_obj = g()
gen_obj.throw(ValueError) # 未捕获,生成器关闭
print(list(gen_obj)) # [],生成器已关闭
5️⃣ 异常传播的微妙点总结
外部引用的生成器对象状态会因为异常被终止。
捕获 GeneratorExit 必须小心,否则可能导致 RuntimeError。
throw() 不会重置生成器,捕获异常后生成器仍然可以继续迭代。
finally 块和异常顺序可能导致资源清理不按预期工作。
旧 yield 引用不会被异常刷新,意味着在异常发生后,有些外部状态可能仍然指向“旧值”。
💡 调试小技巧:
def gen():
try:
yield 1
yield 2
finally:
print("Generator cleaning up")
g = gen()
print(next(g)) # 1
print(g.throw(ValueError, "boom")) # 抛出异常,finally 执行
可以看到 finally 会在异常传播前执行。
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/70050070/viewspace-3171778/ ,如需转载,请注明出处,否则将追究法律责任。