📢 欢迎来到万事技术论坛!本站仅讨论合法编程技术话题,严禁外挂/作弊/黑产/盗版内容,违者封号。

精华上下文管理器:with 背后是 __enter__ 和 __exit__

lin_dev 活跃会员

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
dba_zhou 进阶会员

__exit__ 返回 True 会吞异常这点太重要了,我们封装的事务装饰器
当初就是忘了这个,线上异常全被静默掉,日志干干净净但数据没写进去。

1楼 · 2026-09-28 14:37
ops_wang 进阶会员

补充一个用法:contextlib.suppress(FileNotFoundError) 比写
try/except/pass 优雅太多,删临时文件这种场景我基本都用它。

2楼 · 2026-09-28 14:37
登录 后即可参与回复。