Python 上下文管理器:你真正需要的三种情况

Python context managers: the three cases you actually need. Master with statements, contextlib and custom context managers for better resource management.

  • Go
  • Python
  • MIT
  • 更新于 2026-05-15

{</* 资源信息 */>} 大多数对 Python 上下文管理器的介绍都展示了一个例子 - with open("file.txt") as f: — 到此为止。 就够用了 它们,但它并没有告诉你什么时候“写”一个。 在编写 Python 服务几年后,我不断寻求上下文 管理者在三种特定情况下。 每个问题都解决一个问题 try/finally 在技术上可以解决,但在实践中往往会出错。 ![Python 上下文管理器:您实际需要的三种情况 — dibi8.com](/images/articles/python-context-managers-the- Three-cases-you-actually-need/cover.jpg) ## 情况 1:配对获取和释放 经典案例。 你有一些必须释放的东西—— 一个锁、一个数据库连接、一个临时文件、一个网络套接字——以及 即使中间的代码引发,您也希望确保释放发生。 ````蟒蛇 从 contextlib 导入 contextmanager 导入线程 _lock = 线程.Lock() @contextmanager def critical_section(): _lock.acquire() 尝试: 屈服 最后: _lock.release() 使用 Critical_section(): do_dangerous_thing()

上下文管理器扩展到。 胜利在于“try”/“finally”的存在
在 *helper* 中,而不是调用站点中。 每个来电者都可以免费获得它,并且
没有人会忘记写“finally”块。 当我看到“try: thing.acquire(); 的五个副本”时 ...; 最后:
代码库中的 thing.release()`,我知道有一个上下文管理器在等待
被提取。 ## 情况 2:暂时改变全局状态 这一点很少被谈论,但它是上下文管理器真正的用处
赚取他们的收入。 您想要在一段时间内翻转某些设置
块,并且无论块如何退出,您都希望它恢复到原来的状态。 ````蟒蛇
导入操作系统
从 contextlib 导入 contextmanager @contextmanager
def env(**覆盖): """临时设置环境变量,退出时恢复以前的值。""" 已保存 = {k: os.environ.get(k) for k in overrides} os.environ.update({k: str(v) for k, v in overrides.items()}) 尝试: 屈服 最后: 对于 k,prev 在 save.items() 中: 如果上一个是无: os.environ.pop(k, 无) 别的: os.environ[k] = 上一个 与环境(DEBUG =“1”,REGION =“us-east-1”): 运行测试套件()
# 环境又回到了原来的样子。 ```` 相同的模式适用于“sys.path”、“logging”级别、“decimal”
上下文、模拟属性——“保存、更改、
恢复”形状。 测试尤其受益于这一点; 另一种选择
是在测试过程中出现问题时发生泄漏的装置。 微妙的部分是**正确恢复“None”**。 一个常见的错误是
`os.environ[k] = saving[k]` 不检查 - 写入文字
当变量之前不存在时,将字符串“None”添加到变量中。 总是
将“absent”恢复为“pop”,而不是字符串。 ## 案例 3:抑制您真正想要忽略的异常 有时你确实想吞掉一个特定的异常类并且
继续前进。 Python为此提供了“contextlib.suppress”: ````蟒蛇
从 contextlib 导入抑制 与抑制(FileNotFoundError): os.unlink("maybe-stale.lock")
```` 这比等效的 `try`/` except: pass` 更加清晰,
*因为有限的表面迫使你必须具体*。 你不能
意外地抑制了所有内容——你必须为类命名。 还有你
不能意外地抑制清理下面的代码; `with` 块的
范围正是您所写的。 我发现这对于析构函数和 atexit 处理程序中的清理很有用,其中
你确实无法承担清理本身的费用。 ## 当*不*写一个时 上下文管理器不是免费的。 每个“with”都引入了少量
机械,并且堆叠它们会快速影响可读性。 我避开他们
当: - “获取”的一半实际上并不需要配对的“释放”——只是 调用该函数。 - 清理是尽最大努力并且范围足够小 `try`/`finally` 内联读起来更清晰。 - 正在管理的事物已经由其他事物管理(例如 不要从已经上下文管理的框架中包装“Session” 有自己的生命周期)。 我使用的测试:*“如果我不进行清理工作,下一个人会这样做吗?
默默地泄漏资源?”* 如果是,请编写上下文管理器。 如果没有,则
简单的功能就可以了。 ## 关于异步的注释 在“async”代码中,使用“@asynccontextmanager”和“async with”。 形状
是相同的; 唯一要记住的是你可以在里面“await”
身体,这使得该模式对于诸如
“从池中获取连接,运行查询,然后返回它。” ````蟒蛇
从 contextlib 导入 asynccontextmanager @asynccontextmanager
异步 def 借用(池): conn = 等待池.acquire() 尝试: 产量康涅狄格州 最后: 等待池.释放(连接)
```` 就是这样。 三种模式覆盖了大约 90% 的上下文管理器
我写过。 另外10%很奇怪,一看你就知道了
它。 ## 相关文章 - [Scrapling 回顾:更快、更隐蔽的 Python 抓取](/resources/dev-utils/scrapling-python-stealthy-web-scraping-review/) — 高级 Python 抓取
- [阅读 Postgres 中的 EXPLAIN ANALYZE 而不会迷失](/resources/ai-tools/reading-explain-analyze-postgres/) — 数据库性能优化
- 免费 Claude Code:与任何 AI 提供商免费使用 Claude Code CLI — AI 辅助编码

💬 留言讨论