with 不只是「自动关文件」,它的本质是 __enter__ / __exit__ 协议。
class Timer:
def __enter__(self):
self.t = time.perf_counter()
return self
def __exit__(self, exc_type, exc, tb):
print(f"耗时 {time.perf_counter() - self.t:.3f}s")
return False # 返回 True 会把异常吞掉!
关键点:__exit__ 返回 True 表示「异常我处理了」,异常会被吞掉。
默认返回 None(等价于 False),异常继续往上抛。
不想写类?用 contextmanager
from contextlib import contextmanager
@contextmanager
def timer():
t = time.perf_counter()
try:
yield
finally:
print(f"耗时 {time.perf_counter() - t:.3f}s")
yield 之前是 __enter__,之后是 __exit__。必须用 try/finally,
否则抛异常时收尾代码根本不执行。
三个好用的内置
suppress(ValueError):忽略指定异常,比裸 except 清晰得多nullcontext():给「可选资源」兜底,有就用、没有也不报错ExitStack:动态管理数量不定的上下文
with ExitStack() as stack:
files = [stack.enter_context(open(p)) for p in paths]
判断标准:凡是「开头申请、结尾必须释放」的(锁、连接、临时目录、事务),都该用 with。
楼主 · 2026-09-28 14:37 · 浏览 8

