当前位置: 首页 > article >正文

深入浅出 Python contextlib:优雅管理上下文资源的利器

凌晨三点小陈盯着屏幕上的报错信息头皮发麻。“ResourceWarning: Unclosed file”就这一行警告让他在一堆历史代码里翻了两个小时。打开的文件忘记关了数据库连接没释放临时修改的目录路径也没改回来。代码跑起来没问题但跑久了服务器就开始报“too many open files”。同事老张路过瞥了一眼屏幕“你还在手动写 try...finally 呢用 contextlib 啊几行装饰器的事。”小陈一脸懵“那是什么”这就是很多 Python 开发者都会经历的阶段——被资源管理问题折磨过后才发现标准库里藏着一个宝藏模块。一、从一个没人关的文件说起先看一段最常见的代码f open(data.txt, r) data f.read() print(data) f.close()写过 Python 的人都知道这样写不够安全。如果f.read()中间抛异常f.close()永远不会执行文件句柄就泄露了。于是大家学会了with语句with open(data.txt, r) as f: data f.read() print(data)离开with代码块文件自动关闭不管里面有没有报错。这个with语句背后的原理就是上下文管理器。一个类只要实现了__enter__和__exit__两个魔法方法就能放进with里用。但问题来了每次都要写一个完整的类就为了管理一个资源太啰嗦了。contextlib 就是来解决这个痛点的。二、contextlib 的核心武器contextmanager 装饰器contextmanager是 contextlib 模块里最常用、也最好用的工具。它能把一个普通的生成器函数直接变成上下文管理器。看看怎么用from contextlib import contextmanager contextmanager def managed_file(filename): f open(filename, r) try: yield f finally: f.close() # 使用方式 with managed_file(data.txt) as f: data f.read() print(data)关键点在这里yield前面的代码相当于__enter__yield后面的代码放在finally里相当于__exit__。不管with代码块里发生什么finally都会执行文件一定能关掉。这个写法比写一个完整的类清爽太多了。几行代码搞定逻辑一目了然。三、一个真实的数据库连接场景假设你在写一个 Web 爬虫需要把数据存到 SQLite 数据库。每次操作都要打开连接、获取游标、提交事务、关闭连接写起来非常繁琐def save_data(data): conn sqlite3.connect(app.db) cursor conn.cursor() cursor.execute(INSERT INTO items VALUES (?), (data,)) conn.commit() conn.close()但这样写有几个隐患如果execute报错conn.commit()和conn.close()都不会执行数据库连接就悬在那里了。用contextmanager包装一下contextmanager def get_db(): conn sqlite3.connect(app.db) try: yield conn conn.commit() except Exception: conn.rollback() raise finally: conn.close() # 使用 with get_db() as conn: cursor conn.cursor() cursor.execute(INSERT INTO items VALUES (?), (test,))现在不管代码执行成功还是报错连接都会正确关闭。成功了自动提交失败了自动回滚。一个装饰器把资源管理的复杂度全部封装掉了。四、计时器用上下文做性能监控上下文管理器不只能管理“打开-关闭”类的资源任何“进来时做一件事、出去时做另一件事”的场景都能用。比如你想测一段代码的执行时间import time from contextlib import contextmanager contextmanager def timer(name): start time.time() print(f{name} 开始...) yield elapsed time.time() - start print(f{name} 完成耗时 {elapsed:.2f} 秒) # 使用 with timer(数据清洗): # 这里放你要测的代码 data [i**2 for i in range(1000000)] print(f生成了 {len(data)} 条数据)输出数据清洗 开始... 生成了 1000000 条数据 数据清洗 完成耗时 0.18 秒不需要写一堆start time.time()和print(...)的重复代码。把计时逻辑包进上下文管理器里用的时候一行with timer(...)就搞定了。这对性能调优特别有用。你可以快速给多个代码块加上计时找出瓶颈在哪里。五、临时切换目录用完自动恢复写脚本的时候经常需要临时切换工作目录去处理文件。处理完得切回来不然会影响后面的代码。手动写os.chdir很容易忘记切回来import os os.chdir(/tmp) # 处理临时文件... # 糟糕忘记切回原来的目录了 os.remove(important.txt) # 删错了用 contextlib 包装一下contextmanager def cd(path): old_dir os.getcwd() os.chdir(path) try: yield finally: os.chdir(old_dir) # 使用 with cd(/tmp): # 在 /tmp 目录下操作 with open(temp.txt, w) as f: f.write(临时数据) # 离开 with 块自动切回原目录 # 这里已经回到原来的目录了这个模式可以应用到很多场景临时修改环境变量、临时重定向标准输出、临时禁用信号处理……核心思路都一样进去时保存状态出来时恢复状态。六、更高级的工具ExitStack有时候你需要同时管理多个资源而且这些资源的数量在运行时才能确定。比如打开一批文件files [] for filename in file_list: files.append(open(filename, r)) # 万一中间某个文件打开失败前面已经打开的文件怎么关手动处理会非常麻烦。ExitStack就是为这种场景设计的from contextlib import ExitStack with ExitStack() as stack: files [] for filename in file_list: f stack.enter_context(open(filename, r)) files.append(f) # 所有文件都成功打开继续处理 for f in files: print(f.read()) # 离开 with 块时所有文件按相反顺序自动关闭ExitStack内部维护了一个栈。每次调用enter_context它就把这个资源记下来。离开with块时按照后进先出的顺序自动清理所有资源。不管中间哪个步骤出问题已经成功打开的资源都会被正确关闭。还有一个常用场景在旧代码里有些资源不是上下文管理器只有close方法。ExitStack也能处理with ExitStack() as stack: conn stack.callback(lambda: db.close()) # 离开时 db.close() 会被自动调用callback方法让你可以注册任意的清理函数非常灵活。七、suppress忽略你不关心的异常有时候你并不想处理某个异常只想让它静悄悄地过去。比如删除一个可能不存在的文件try: os.remove(temp.txt) except FileNotFoundError: pass写try-except-pass太啰嗦了。contextlib.suppress专门解决这个问题from contextlib import suppress with suppress(FileNotFoundError): os.remove(temp.txt)可以同时抑制多种异常with suppress(FileNotFoundError, PermissionError): os.remove(temp.txt)代码干净了很多意图也很明确“这个异常出现了也没关系忽略它。”八、nullcontext需要但不需要管理的时候写函数的时候有时候需要根据参数决定是否使用上下文管理器。比如调试模式下打开日志文件生产模式下什么都不做def process_data(debugFalse): if debug: manager open(debug.log, w) else: manager ??? # 这里放什么 with manager as log: log.write(处理中...)nullcontext就是那个“什么都不做”的上下文管理器from contextlib import nullcontext def process_data(debugFalse): if debug: manager open(debug.log, w) else: manager nullcontext() with manager as log: # 如果 debugTruelog 是文件对象 # 如果 debugFalselog 是 None但代码仍然可以正常执行 if log is not None: log.write(处理中...) # 实际业务逻辑 print(数据处理完成)这样你就不需要写两套逻辑一套带with一套不带。统一用with结构nullcontext会乖乖地什么也不做。九、把多个上下文管理器串起来Python 3.10 之后contextlib提供了一个更简洁的写法。以前你要嵌套多个withwith open(input.txt) as infile: with open(output.txt, w) as outfile: outfile.write(infile.read())现在可以写成一行with ( open(input.txt) as infile, open(output.txt, w) as outfile ): outfile.write(infile.read())括号把多个上下文管理器包在一起Python 会自动按顺序进入、按相反顺序退出。代码层级少了一层看起来舒服很多。十、实战封装一个重试机制把这些技巧组合起来能做出很实用的工具。比如一个带重试功能的上下文管理器import time from contextlib import contextmanager contextmanager def retry(max_attempts3, delay1): last_exception None for attempt in range(max_attempts): try: yield return # 成功就退出 except Exception as e: last_exception e print(f第 {attempt 1} 次尝试失败: {e}) if attempt max_attempts - 1: time.sleep(delay) raise last_exception # 使用 with retry(max_attempts5, delay2): # 这里放可能会临时失败的代码 response requests.get(https://unstable-api.example.com/data) response.raise_for_status()这个上下文管理器会自动重试 5 次每次失败等 2 秒。如果 5 次都失败抛出最后一次的异常。调用方的代码非常干净不需要写任何重试逻辑。这就是 contextlib 的魅力——把横切关注点cross-cutting concerns封装起来让业务代码保持简洁。十一、contextlib 的底层原理用contextmanager装饰一个生成器函数Python 背后做了这些事调用函数得到一个生成器对象调用生成器的__next__()执行到yield暂停yield的值作为__enter__的返回值执行with代码块代码块正常结束或抛出异常时调用生成器的throw()或__next__()执行yield后面的代码生成器执行结束所以yield后面的代码一定要放在try-finally里确保不管with块里发生了什么清理逻辑都能执行。这个设计非常巧妙。生成器本来是用来产生序列的Python 把它复用到上下文管理器的场景用yield切开了“进入”和“退出”两个阶段。写在最后回头再看小陈的故事。如果当时他知道contextmanager打开数据库连接的那段代码会写成这样contextmanager def get_conn(): conn create_conn() try: yield conn finally: conn.close()三个函数调用一个yield一个finally搞定。不用写类不用记__enter__和__exit__代码意图清晰得不能再清晰。contextlib 这个模块不大但每一行代码都经过精心设计。它解决的是一个很具体的问题——让with语句的编写变得简单。但因为这个“具体问题”在编程里几乎天天遇到它的价值就被无限放大了。下次你再遇到需要“进去时做点事、出来时做点事”的场景先想想能不能用contextmanager包装一下。写出一个漂亮的上下文管理器那种“用起来真舒服”的感觉比写一百行注释都来得实在。彩蛋contextlib还有AbstractContextManager、AsyncContextDecorator等高级工具适合在写框架或库的时候使用。不过对于 90% 的日常开发contextmanager加上ExitStack、suppress、nullcontext这四个工具已经足够应对绝大多数资源管理问题了。

相关文章:

深入浅出 Python contextlib:优雅管理上下文资源的利器

凌晨三点,小陈盯着屏幕上的报错信息,头皮发麻。“ResourceWarning: Unclosed file”就这一行警告,让他在一堆历史代码里翻了两个小时。打开的文件忘记关了,数据库连接没释放,临时修改的目录路径也没改回来。代码跑起来…...

太烧token了,我用Ai写了一个vscode的插件wps-editor(已开源)

这是一篇关于开源项目Wps-Editor的介绍文章,希望能让大家了解它的价值并支持其发展。 引言 在人工智能(AI)浪潮席卷各行各业的今天,大型语言模型(LLM)已成为内容创作者、办公人士、学生乃至研究者的得力助手。无论是撰写报告、分析数据、润色文案&#…...

推荐 React 开发需要在 VS Code 中安装的插件

React开发必备VSCode插件清单:核心工具包括ESLintPrettier保障代码质量与风格统一;ES7React代码片段和PathIntellisense提升编码效率;ReactDeveloperTools辅助UI调试。关键配置要点:1)用eslint-config-prettier解决ESLint与Pretti…...

原料杂乱难管理?合并功能一键搞定

在制造行业的日常运营中,进销存管理的核心痛点往往藏在细节里——尤其是生产环节的领料流程,却常常成为拖慢效率、造成损耗的“隐形绊脚石”。很多企业在生产计划落地时,都会遇到这样的困境:同一份生产计划单中,不同成…...

3步永久保存青春记忆:GetQzonehistory让QQ空间数据永不消逝

3步永久保存青春记忆:GetQzonehistory让QQ空间数据永不消逝 【免费下载链接】GetQzonehistory 获取QQ空间发布的历史说说 项目地址: https://gitcode.com/GitHub_Trending/ge/GetQzonehistory 你的数字回忆正在流失吗? 每天有超过10万条QQ空间动…...

4个核心预训练模型应用指南:从资源获取到问题诊断

4个核心预训练模型应用指南:从资源获取到问题诊断 【免费下载链接】so-vits-svc SoftVC VITS Singing Voice Conversion 项目地址: https://gitcode.com/gh_mirrors/so/so-vits-svc 预训练模型是so-vits-svc实现高质量语音转换的基础组件,这些经过…...

DownKyi:3分钟学会B站视频下载的终极免费方案

DownKyi:3分钟学会B站视频下载的终极免费方案 【免费下载链接】downkyi 哔哩下载姬downkyi,哔哩哔哩网站视频下载工具,支持批量下载,支持8K、HDR、杜比视界,提供工具箱(音视频提取、去水印等)。…...

3个步骤彻底释放惠普游戏本性能:OmenSuperHub终极指南

3个步骤彻底释放惠普游戏本性能:OmenSuperHub终极指南 【免费下载链接】OmenSuperHub 使用 WMI BIOS控制性能和风扇速度,自动解除DB功耗限制。 项目地址: https://gitcode.com/gh_mirrors/om/OmenSuperHub 还在为官方Omen Gaming Hub的臃肿体积和…...

BiliBiliCCSubtitle:B站字幕高效解决方案,解决字幕获取、格式转换与批量处理难题

BiliBiliCCSubtitle:B站字幕高效解决方案,解决字幕获取、格式转换与批量处理难题 【免费下载链接】BiliBiliCCSubtitle 一个用于下载B站(哔哩哔哩)CC字幕及转换的工具; 项目地址: https://gitcode.com/gh_mirrors/bi/BiliBiliCCSubtitle 在数字内…...

才聚:国内最早从事PMP培训的机构

在项目管理职业资格认证领域,PMP(项目管理专业人士)证书已成为衡量项目经理能力的重要标准。面对市场上众多的PMP培训机构,如何选择一个真正有历史沉淀、专业实力和考试服务能力的机构,成为考生最关心的问题。本文将从…...

项目经理的最高境界,是学会“睁一只眼闭一只眼”

在项目管理圈子里,流行着一种近乎悖论的说法:一个真正优秀的项目经理,往往不是那些事无巨细、火眼金睛的“细节狂魔”,而是懂得适时“睁一只眼闭一只眼”的“智慧型管理者”。 这听起来似乎与PMP(项目管理专业人士&…...

Google Stitch + MCP:AI 时代的“设计即代码“新范式

从模糊需求到可运行应用,只需 3 小时——这不是科幻,而是正在发生的 AI 编程革命。 引言:当设计遇见代码 在 AI 编程工具百花齐放的今天,开发者们面临着一个尴尬的现实:工具越多,上下文越碎。 想象一下这…...

Teensy USB主机协议栈USBHost_t36深度解析

1. USBHost_t36:面向 Teensy 3.6 与 Teensy 4.x 的嵌入式 USB 主机协议栈深度解析 USB 主机功能在嵌入式系统中长期处于“高门槛、低普及”状态。传统 MCU 往往缺乏专用 USB OTG 控制器,或需依赖复杂 BSP 与庞大中间件(如 USBX、LUFA Host St…...

新能源/电力系统论文中的应用及盲审注意事项

在新能源/电力系统方向学术论文研究中,气象数据的权威性、精度及适配性直接影响论文盲审结果。羲和能源气象大数据平台作为该领域常用的气象数据支撑工具,其数据处理流程、适配特性与学术规范适配性较强,可有效提升论文盲审通过率。本文结合盲…...

WPF新手村教程(七)—— 终章(MVVM架构初见杀)

前言 在使用 kubectl get $KIND -o yaml 查看 k8s 资源时,输出结果中包含大量由集群自动生成的元数据(如 managedFields、resourceVersion、uid 等)。这些信息在实际复用 yaml 清单时需要手动清理,增加了额外的工作量。 使用 ku…...

实测2026最强Agent!非结构化数据处理谁才是王者?实在Agent深度拆解

摘要: 步入2026年,AI智能体(Agent)已从简单的对话窗口进化为具备自主规划与执行能力的“数字员工”。然而,面对企业内部占比超过80%的非结构化数据(如扫描件、复杂网页、旧系统UI、音视频等)&am…...

收藏!小白也能看懂RAG,让大模型拥有外部知识库的翅膀

当大模型遇到"不知道"的问题,RAG 让它拥有了外部知识的翅膀。大型语言模型(LLM)虽然知识渊博,但存在两个致命短板:知识截止和幻觉问题。模型训练完成后,新发生的事情它一无所知;被问到…...

SEO 优化工具如何进行本地优化

SEO 优化工具如何进行本地优化 在当今数字化时代,本地优化成为了企业和个人网站在百度搜索中获得高排名的关键因素之一。本地优化,即通过特定策略提升一个网站在特定地理位置的搜索排名,这对于希望在本地市场中获得更多流量的企业尤为重要。…...

公共部门人力资源管理、公共行政学、公共经济学(自考速记核心概念)

公共部门人力资源管理、公共行政学、公共经济学(自考速记核心概念) 第一页(核心基础规划与获取) 一、核心基础概念(必背) 1.公共部门人力资源管理:公共部门(政府、事业单位、非营…...

数据仓库大规模数据处理:海量存储与高效访问实战优化方案

数据仓库大规模数据处理:海量存储与高效访问实战优化方案一、引言二、核心挑战:大规模数据带来的3大问题三、整体解决方案流程图(海量数据存储与访问)四、一、大规模数据存储优化方案(核心)4.1 方案1&#…...

大模型“幻觉“频现?RAG技术如何根治三大痛点,实现精准问答?

文章深入解析了RAG(检索增强生成)技术的核心原理与实现流程,指出大模型普遍存在的三大缺陷:幻觉现象、知识更新缓慢以及领域知识理解有限。RAG通过结合向量数据库、嵌入模型和大语言模型,实现从外部私有知识库检索信息…...

AI 短剧变现的 4 大合规赛道 新手低门槛可切入

当下AI短剧成为内容领域的热门风口,不少人想入局分一杯羹,却因担心踩坑违规、找不准变现方向而犹豫不决。其实新手入局无需焦虑,只要选对合规赛道,低门槛也能轻松切入。本文将详细拆解4个核心变现路径,全程贴合平台审核…...

2026年脱模油供应商怎么选?这几点很关键

2026年,建筑行业持续发展,脱模油作为建筑施工中不可或缺的材料,其质量和适用性至关重要。关云建材在脱模油领域深耕多年,积累了丰富的行业经验。接下来,我们就来深入探讨脱模油的相关问题,帮助大家选到合适…...

3种突破Cursor Pro限制的创新方案:解锁AI编程全功能体验

3种突破Cursor Pro限制的创新方案:解锁AI编程全功能体验 【免费下载链接】cursor-free-vip [Support 0.45](Multi Language 多语言)自动注册 Cursor Ai ,自动重置机器ID , 免费升级使用Pro 功能: Youve reached your t…...

2026届必备的AI辅助写作平台解析与推荐

Ai论文网站排名(开题报告、文献综述、降aigc率、降重综合对比) TOP1. 千笔AI TOP2. aipasspaper TOP3. 清北论文 TOP4. 豆包 TOP5. kimi TOP6. deepseek 人工智能论文工具正渐渐在学术写作流程里掺杂进来,变成研究者提高效率的管用帮手…...

Linux 驱动开发流程(带最小可运行代码 + 通俗类比)

Linux 驱动开发流程(带最小可运行代码 通俗类比) 很多人学 Linux 驱动都会卡在这里:API 都看过,但完全不知道它们是怎么串起来工作的这篇文章目标很明确: ✅ 用一条主线讲清流程 ✅ 用类比帮你记住 ✅ 给你一个最小可…...

华硕笔记本性能调校新纪元:GHelper如何重塑硬件控制体验

华硕笔记本性能调校新纪元:GHelper如何重塑硬件控制体验 【免费下载链接】g-helper Lightweight, open-source control tool for ASUS laptops and ROG Ally. Manage performance modes, fans, GPU, battery, and RGB lighting across Zephyrus, Flow, TUF, Strix, …...

2026AI大模型入门学习教程(建议收藏),大模型入门学习路线,非常详细看这一篇就够了!

一、LLM Fundamentals 基础 1. 机器学习的数学基础 在掌握机器学习之前,理解支撑这些算法的基本数学概念非常重要。 线性代数:这是理解许多算法(特别是深度学习算法)的关键。主要概念包括向量、矩阵、行列式、特征值和特征向量、…...

哪款蓝牙耳机性价比比较高?2026年十大高性价比蓝牙耳机推荐!

现在蓝牙耳机这玩意儿,基本上人手一副了吧?上班路上、健身房、甚至睡觉前都得挂着,早就不是啥稀罕物件了。但正因为太普及了,市场上也是啥妖魔鬼怪都有。最典型的毛病就是“价格虚标”,先定个七八百的指导价&#xff0…...

一体化数字引擎 驱动机械设备非标项目精益盈

机械设备行业正进入深度数字化转型期,非标定制与项目型制造企业普遍面临项目成本失控、进度不透明、变更响应慢、售后价值难挖掘等核心痛点。传统分散式管理与复杂业务场景脱节,导致交付延迟、利润流失、协同效率低下。面向非标设备行业的数字化管理需求…...