一名 PHP 工程师的 Python 系统学习笔记(06):with 上下文管理器
真正把 Python 写“干净”,with 是绕不过去的一关。
很多人对 with 的理解只停留在:
with open("a.txt") as f: data = f.read()
能用,但不知道为什么要用。
这一篇只做一件事:把 with 在工程里的价值讲清楚。
一、先抛结论:with 解决的是什么问题?
一句话总结:
保证资源一定被正确释放。
不管中间代码:
资源都能被回收。
二、不用 with 的典型问题
f = open("a.txt")try: data = f.read()finally: f.close()
这段代码本身没错,但有 3 个问题:
👉 with 的意义,是把这类模板代码抽走。
三、with 的本质:上下文管理协议
任何对象,只要实现了这两个方法,就能配合 with 使用:
__enter__()__exit__(exc_type, exc, tb)
执行顺序只有一句话:
进入时调用 __enter__,离开时一定调用 __exit__。
四、自己实现一个最小上下文管理器
classTimer:def__enter__(self): self.start = time.time()return selfdef__exit__(self, exc_type, exc, tb): cost = time.time() - self.start print(f"cost: {cost}")
with Timer(): work()
工程意义:
👉 用语法结构,约束资源的生命周期。
五、异常在 with 中是如何处理的?
__exit__ 的返回值,决定异常是否继续抛出:
def__exit__(self, exc_type, exc, tb):returnTrue# 吞掉异常
工程建议:
👉 99% 的情况下,不要吞异常。
with 的职责是清理资源,不是隐藏错误。
六、contextlib:更轻量的写法
from contextlib import contextmanager@contextmanagerdeftimer(): start = time.time()yield print(time.time() - start)
with timer(): work()
在 @contextmanager 语义下: yield 之前 → enteryield 之后 → exit
with timer(): ↓ yield ↓ 离开 with
这是 “生成器 + 装饰器 + 异常处理” 的组合体。
👉 非常 Pythonic。
七、工程中常见的 with 场景
你在项目里见到的这些,几乎都是上下文管理器:
八、这一篇你只需要记住 5 件事
写在最后
如果你已经理解了:
那么你已经掌握了 Python 工程级语法的核心三件套。
下一篇,我会继续写一个和性能、可维护性高度相关的话题:
迭代器与生成器:为什么 Python 能写得这么省内存
如果你也是从 PHP / Go 转 Python,希望这个系列能真正形成你的“第二语言直觉”。