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

为何基于@contextmanager实现的上下文管理器遇异常不退出?

为什么用@contextmanager实现的上下文管理器在异常时未关闭文件?

问题出在你用@contextmanager装饰的生成器没有处理with块内抛出的异常,导致yield之后的清理代码(关闭文件)根本没机会执行。

核心原因:@contextmanager的工作逻辑

@contextmanager装饰器是把生成器包装成上下文管理器的语法糖,它的执行规则是:

  1. 执行yield之前的代码(对应类实现的__enter__)
  2. yield返回的对象绑定给as后的变量
  3. 当with块正常退出时,生成器会继续执行yield之后的代码;但如果with块内抛出异常,这个异常会直接传递给生成器——如果生成器没有捕获这个异常,yield之后的代码会被直接跳过,终止执行。

而你写的生成器里,with块抛出Exception后,异常直接打断了生成器,f.close()和print('closed')完全没运行,所以文件没被关闭,后续还能继续读取内容。

反观类实现的上下文管理器:__exit__方法是Python上下文管理器协议规定的必执行逻辑——不管with块是正常退出还是抛出异常,__exit__都会被调用,所以文件能被正常关闭。

修复方法

给生成器里的yield加上try/finally,确保清理逻辑无论是否发生异常都会执行:

from contextlib import contextmanager

@contextmanager
def open_read(path: str):
    f = open(path, 'r')
    print('open')
    try:
        yield f
    finally:
        f.close()
        print('closed')

这样修改后,不管with块内是正常执行还是抛出异常,finally块里的关闭代码都会运行,之后再读取文件就会触发ValueError: I/O operation on closed file.,符合你的预期。

内容的提问来源于stack exchange,提问作者Eyal Kutz

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 07:22:52