Python 上下文管理器
with 语句配对资源获取与释放;实现 __enter__/__exit__ 或使用 contextlib。
#type / concept
#status / growing
#tech / dev
#resource / python
#tech / lang / python
[!info] 关联笔记
Python 上下文管理器
这个概念为什么出现
文件、锁、事务、临时目录必须“借出后归还”。只靠程序员记得 close() 会在异常路径泄漏。with 把获取/释放写成结构。
[!abstract] 一句话理解 上下文管理器实现获取与释放协议;
with保证离开块时调用清理逻辑,即使块内异常。
最小可运行示例
先把示例放进业务场景,再看代码:
场景:库存预留锁必须释放
# reserve_lock_cm.py
# 业务意图:预留库存时持有锁,结束或失败都释放。
# 教学点:
# - __enter__/__exit__;
# - 异常仍触发 exit;
# - exit 返回 False 传播异常。
class InventoryLock:
def __init__(self, sku: str) -> None:
self.sku = sku
self.held = False
def __enter__(self) -> "InventoryLock":
self.held = True
print(f"lock acquire {self.sku}")
return self
def __exit__(self, exc_type, exc, tb) -> bool:
self.held = False
print(f"lock release {self.sku}; exc={exc_type.__name__ if exc_type else None}")
return False # 不吞异常
def reserve(sku: str, ok: bool) -> None:
with InventoryLock(sku) as lock:
print("held?", lock.held)
if not ok:
raise RuntimeError("upstream fail")
print("reserved")
def main() -> None:
reserve("SKU-1", True)
try:
reserve("SKU-2", False)
except RuntimeError as exc:
print("caught:", exc)
if __name__ == "__main__":
main()
建议运行:
python reserve_lock_cm.py
期望输出:
lock acquire SKU-1
held? True
reserved
lock release SKU-1; exc=None
lock acquire SKU-2
held? True
lock release SKU-2; exc=RuntimeError
caught: upstream fail
结合场景再看三个关注点
- 失败路径也释放锁——
__exit__仍执行。 - 返回 False 让异常继续传播给调用方。
- 真实项目可用
threading.Lock/contextlib。
核心概念与准确模型
with cm as x:
...
# 约等于 enter; try: body; finally: exit
contextlib.contextmanager:生成器写 CM- 多上下文:
with A() as a, B() as b: - 异步:
async with+__aenter__/__aexit__
边界情况与反直觉行为
__exit__返回 True 会吞掉异常——极少需要。- enter 失败不会调用 exit。
- 可重入/可重入锁语义需文档化。
常见误区
[!warning] 常见误区:with 只是语法糖可有可无 错误理解:finally 手写一样。
正确模型:协议化清理减少遗漏,是资源安全默认写法。
工程实践
- 文件:
with open(...) - 自研资源优先 CM
- 测试断言异常后资源状态
本节总结
上下文管理器把生命周期变成语法结构。有获取就有释放,异常不是借口。
自测题
- 块内异常时
__exit__会执行吗? - 何时用
contextlib.contextmanager?
参考答案
- 会(只要 enter 成功)。
- 逻辑简单、想用生成器 yield 前后写获取/释放时。
延伸阅读与资料来源
| 资料 | 类型 | 支撑内容 |
|---|---|---|
| with statement | 规范 | 语法 |
| contextlib | 文档 | 工具 |