上下文管理器本身开销极小,真正影响性能的是__enter__和__exit__中的逻辑;空with块仅慢3%~5%,i/o或计算操作会完全掩盖该差异;高频场景应手写类并复用实例;async with开销为同步版3~10倍,仅适用于真实异步i/o。

上下文管理器本身开销很小,但 __enter__ 和 __exit__ 里的逻辑决定实际成本
Python 的 with 语句本身几乎不拖慢执行——它只是在字节码层面插入几条跳转指令,比手动写 try/finally 略重一点,但差不到一个数量级。真正吃性能的是你放进 __enter__ 和 __exit__ 里的代码。
- 常见高开销场景:在
__enter__中做网络请求、打开大文件、解析 JSON;在__exit__中做同步刷盘、发日志、关数据库连接 - 如果只是简单资源标记(比如
threading.Lock的获取/释放),那基本可忽略 - 用
timeit测过:空with块比直接执行同代码慢约 3%~5%,但一旦涉及 I/O 或计算,这点差异就完全被淹没了
contextlib.contextmanager 装饰器比手写类稍慢,但差距只在微秒级
用 @contextmanager 写的生成器上下文管理器,底层会包装成 _GeneratorContextManager 实例,多了协程状态切换和异常传播的间接层。不过实测中,单次进入/退出耗时多出 0.2~0.8 μs,对绝大多数应用毫无感知。
- 适合快速原型、临时封装、逻辑简单且不高频调用的场景
- 若上下文被每毫秒调用数百次(如高频采样、游戏帧循环),建议手写类并复用实例,避免反复创建生成器对象
- 注意:装饰器版本无法被
isinstance(obj, ContextManager)准确识别,类型检查工具可能报错
嵌套多层 with 不会指数级变慢,但会线性增加栈深度和异常处理开销
每层 with 对应一个 __exit__ 调用链,Python 会按逆序逐个执行。这不是“嵌套越深越卡”,而是“层数越多,__exit__ 总调用次数越多”。
- 典型错误:在循环里层层
with open(...)+with sqlite3.connect(...)+with lock:—— 这不是语法问题,是资源创建/销毁频次过高 - 更优做法:把外层不变的上下文提到循环外(比如连接复用),只在必要时才进内层
- 极端情况(>10 层嵌套)可能触发 CPython 的栈保护机制,抛
RecursionError,但这属于设计缺陷,不是性能瓶颈
异步上下文管理器(async with)的开销明显高于同步版
async with 涉及事件循环调度、协程挂起/恢复、__aenter__/__aexit__ 的 await 链,每次进入至少多一次事件循环 tick。实测单次耗时通常是同步版的 3~10 倍。
立即学习“Python免费学习笔记(深入)”;
- 不要用
async with包裹纯 CPU 操作(比如async with io.BytesIO(...) as f:)——没意义还更慢 - 真正该用它的场景只有:HTTP 客户端、异步 DB 驱动、WebSocket 连接等必须等待 I/O 的地方
- 注意:
async with块内不能混用阻塞调用(如time.sleep()),否则会阻塞整个 event loop,这比性能问题更致命
with 中重新创建——这时候删掉 with 也救不了性能。











