You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python @contextmanager装饰的上下文管理器为何会屏蔽异常?

问题原因

你用@contextmanager实现的上下文管理器异常被屏蔽,核心原因是在finally代码块中写了return语句,这触发了Python本身的异常屏蔽规则。

底层逻辑说明

  • 被@contextmanager装饰的生成器函数,执行到yield关键字时会暂停运行,将控制权交给with块内的业务代码。
  • 当with块内抛出异常时,该异常会被重新注入到生成器暂停的yield位置,沿着生成器的调用栈向外传播。
  • Python语法有明确规则:如果finally块中存在return、break这类会跳出当前代码块的控制流语句,try结构中未处理的异常会被直接丢弃,不会继续向外抛出。

你可以用最简单的普通函数验证这个规则:

def demo():
    try:
        raise ValueError("测试异常")
    finally:
        return

调用上述demo()函数时,不会抛出任何异常,ValueError会被finally里的return直接吞掉。

回到你的上下文管理器代码:with块抛出MyException后,异常回到生成器的yield断点,进入finally块执行return,直接把待传播的MyException丢弃,生成器正常执行结束,外层的except MyException分支自然捕获不到异常,就会走到打印How did I get here ???的逻辑。

为什么nullcontext不会有问题

nullcontext是标准库提供的空上下文管理器,进入和退出阶段不会做任何干扰异常传播的操作,with块抛出的异常会原封不动向外传递,因此能被外层except分支正常捕获。

正确写法

如果不需要在上下文管理器退出阶段做特殊的清理逻辑,直接去掉finally块和无意义的return即可:

@contextmanager
def annotate():
    yield None

如果确实需要用finally块做资源释放等清理操作,只在finally块中写清理逻辑即可,绝对不要在finally块中写return/break这类会中断控制流的语句,避免异常被意外屏蔽。


内容的提问来源于stack exchange,提问作者Boaz Nahum

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 21:48:10