Python 的 with 语句依赖上下文管理器协议,要求对象实现 enter 和 exit 方法:前者在进入时调用并返回绑定值,后者在退出时必执行以确保资源清理,且可选择抑制异常。

Python 的 with 语句背后依赖的是“上下文管理器”(Context Manager)协议,核心是对象必须实现 __enter__ 和 __exit__ 这两个特殊方法。它不是语法糖,而是一套明确的运行机制:进入时自动调用 __enter__,退出时(无论是否异常)必定调用 __exit__,从而保证资源安全释放。
上下文管理器的两个关键方法
__enter__ 在 with 语句开始执行时被调用,它的返回值会绑定到 as 后的变量(如果写了的话)。__exit__(exc_type, exc_value, traceback) 在代码块结束时被调用,接收异常信息三元组;若该方法返回 True,则异常被吞掉,不会向外传播。
-
__enter__可以做资源获取、状态设置等初始化操作 -
__exit__必须处理清理逻辑,比如关闭文件、回滚事务、释放锁 - 即使
with块中发生未捕获异常,__exit__也一定会执行
手动实现一个上下文管理器
只要定义了上述两个方法,类实例就能用于 with 语句。例如,一个简单的计时器:
class Timer:
def __enter__(self):
self.start = time.time()
return self
<pre class="brush:php;toolbar:false;">def __exit__(self, exc_type, exc_val, exc_tb):
self.end = time.time()
print(f"耗时: {self.end - self.start:.2f} 秒")
return False # 不抑制异常
立即学习“Python免费学习笔记(深入)”;
使用时:with Timer() as t:,t 就是 __enter__ 的返回值(即 self)。
@contextmanager 装饰器简化写法
对于简单场景,不用写完整类,可用 contextlib.contextmanager 装饰器把生成器函数变成上下文管理器。函数中 yield 之前的代码相当于 __enter__,之后的相当于 __exit__(支持 try/finally 保证执行)。
from contextlib import contextmanager <p>@contextmanager def open_file(name, mode): f = open(name, mode) try: yield f finally: f.close() </p>
这样就能写 with open_file('a.txt', 'r') as f:,逻辑清晰且不易漏掉 close。
常见内置上下文管理器与陷阱
标准库中很多类型原生支持上下文协议,比如 open()、threading.Lock、tempfile.TemporaryDirectory。但要注意:
-
file.__exit__会静默忽略OSError(如关闭已关闭的文件),但其他异常仍会抛出 - 多个上下文管理器可写在同一行:
with open(a) as f1, open(b) as f2:,它们按顺序进入、逆序退出 - 自定义类若只实现了
__exit__而没写__enter__,with会报错










