bigywater.tech
Python · 7

with 语句:上下文管理器如何保证资源释放

用自定义__enter__/__exit__验证正常退出、内部抛异常、__exit__吞掉异常三种场景,看with语句是怎么保证资源释放的。

3种场景
正常/异常/吞掉异常,__exit__都会被调用
exc_type传递
__exit__能拿到发生了什么异常
返回True
能让__exit__悄悄吞掉异常

做的事:一个自定义上下文管理器,记录每一步发生了什么

实现__enter__和__exit__,让每次调用都打印出来。

代码class Resource: def __enter__(self): print("__enter__ 被调用,资源已打开") return self def __exit__(self, exc_type, exc_val, exc_tb): print(f"__exit__ 被调用,exc_type={exc_type}") print("资源已释放(不管上面是否出异常)") return self.suppress

验证:三种场景,__exit__都被调用了

正常退出、内部抛异常、__exit__选择吞掉异常——三种的运行轨迹。

场景1:正常退出,输出[A] __enter__ 被调用,资源已打开 with块内部在执行 [A] __exit__ 被调用,exc_type=None [A] 资源已释放 场景2:内部抛出ValueError,输出[B] __enter__ 被调用,资源已打开 with块内部在执行 [B] __exit__ 被调用,exc_type=<class 'ValueError'> [B] 资源已释放 在with外面捕获到异常: 模拟一个错误 场景3:__exit__返回True,吞掉异常[C] __enter__ 被调用,资源已打开 with块内部在执行 [C] __exit__ 被调用,exc_type=<class 'RuntimeError'> [C] 资源已释放 程序走到这里,外面完全没有捕获到任何异常

三种场景里,__exit__ 都被调用了——不管with块内部是正常结束还是抛出了异常。这就是with语句提供的保证:资源清理代码总能执行到,不需要你手写try/finally。

场景3揭示了一个容易被忽略的细节:__exit__ 如果返回 True,这个异常就会被吞掉,不会再往外传播——外层代码完全感知不到曾经发生过异常。这是个强大但也危险的能力:如果不是刻意要实现"忽略特定异常"的逻辑(比如 contextlib.suppress),不应该让 __exit__ 无脑返回True,否则会悄悄吞掉本该暴露出来的bug。

分步演示:__exit__保证清理代码一定会跑

每一步都是本文上面用Python 3.14跑出来的原始输出。

不管正常还是异常退出,__exit__都会被调用
这一步新验证的场景

    本文用到的工具

    • 本机 Python 3.14.7
    • 自定义 __enter__ / __exit__:验证with语句的三种退出场景
    京ICP备2026067502号-1京公网安备11010502063487号