Python上下文管理器精讲:with语句原理、contextlib与实战陷阱
很多人写了很久的Python用with open(...) as f收发文件、用with lock:保护共享资源却始终没搞清楚with背后的协议到底是什么。我也是在踩了几次“看起来能跑、一换场景就炸”的坑之后才把上下文管理器这块彻底啃透。这篇文章就把Python上下文管理器with语句的原理与实践从协议栈到业务场景一次性讲清楚包括__enter__/__exit__的真实调用链、异常传递机制、contextlib里那些不起眼的宝贝以及我在真实项目里反复踩过的几个陷阱。适合刚学完Python基础、对with只有模糊印象的初学者也适合写了不少业务代码但没有系统梳理过这块的进阶开发者。内容不长篇大论地背文档而是围绕“为什么要这样设计”“实际跑起来会发生什么”“业务里怎么用好”这三件事展开。1. 先把资源管理的痛点摆出来为什么非要有个with1.1 手动try/finally的样板代码有多烦假设你在写一个模块需要把内存里的数据定期写入磁盘。第一版代码几乎都会长这样f open(data.txt, w, encodingutf-8) try: f.write(第一次写入\n) f.write(第二次写入\n) finally: f.close()这段代码本身没有任何错误它保证了close()一定会执行。问题在于每次打开资源都要配一套try/finally逻辑一多就是大片的样板结构。更麻烦的是如果代码里同时涉及文件、线程锁、数据库游标三种资源三层嵌套的try/finally缩进会直接让可读性崩掉。我见过一段历史代码函数里开了两个文件、一把锁、一个数据库连接嵌套的try/finally层层叠叠为了定位一个小bug翻了好几分钟。其实这样的代码本来可以用with干干净净地解决只是当时的作者没有意识到上下文管理器那个“收尾”动作可以被标准化。1.2 with语句只是一个语法糖吗很多人把with理解成“自带close的语法糖”这样说只对了一半。with的本质是用协议约束了三个动作的执行顺序进入时的准备、代码块的运行、退出时的清理。无论中间的业务代码是正常返回、中途return还是抛异常退出清理这一段都保证会被执行。看一个简单的自定义类来理解这个“协议”class DemoResource: def __enter__(self): print(1. enter 阶段) return self def __exit__(self, exc_type, exc_val, exc_tb): print(4. exit 阶段) return False with DemoResource() as res: print(2. 业务代码执行中) print(3. 业务代码结束)运行结果会按“1 - 2 - 3 - 4”的顺序输出。with DemoResource() as res这行其实在背后做了三件事调用DemoResource()构造对象、调用对象的__enter__()方法、把__enter__()返回的值绑定给res。很多新人以为是“把对象绑定给res”错了绑定的是__enter__()的返回值这个返回值不一定是原对象本身。1.3 三大必需条件想要一个对象支持with它必须满足对象实现了__enter__用来准备资源并返回需要操作的对象对象实现了__exit__用来清理资源并接收异常信息对象的__exit__返回布尔值决定了异常是否被吞掉。三个条件缺一个with就会直接抛AttributeError。比如列表就没有__enter__方法所以with [1,2,3]:是跑不通的。这种设计让with不仅适用于文件线程锁、数据库事务、网络会话、临时环境变量等等都能纳入同一套管理逻辑这也是上下文管理器值得单独写一篇的原因。2. 揭开__enter__与__exit__的细节异常到底怎么传递2.1 方法签名和返回值的真实语义__exit__的完整签名是def __exit__(self, exc_type, exc_val, exc_tb): ...三个参数分别表示异常类型、异常实例、异常的追踪栈。业务代码正常运行没有异常时这三个参数全部是None。一旦业务代码或__enter__之后的代码抛出异常Python解释器就会把异常信息打包传给__exit__。__exit__的返回值很关键返回False或None表示“异常我没处理”解释器继续向外抛出返回True表示“异常我已经吞掉了”业务代码的异常不会继续向更外层传播。这里我吃过一次亏。某个自定义的连接池管理器在__exit__里把异常记录到日志后直接return True结果调用方完全感知不到操作失败数据写失败也被当成成功。为了调试这个问题好几个人排查了很久才定位到是异常被悄悄吞了。所以正常情况下__exit__应该返回False除非你有极其明确的需求。2.2 一个完整异常链路的演示用一个类演示异常发生时发生了什么class CleanupDemo: def __enter__(self): print(进入资源) return self def __exit__(self, exc_type, exc_val, exc_tb): print(f收到异常信息{exc_type}) print(清理资源) return False with CleanupDemo(): raise ValueError(手动抛出的异常)运行时会看到先打印进入资源然后收到异常信息再打印清理资源最后程序抛出ValueError并中断。这说明__exit__在异常传播路径上起到了“中间层”的作用它能观察到异常却不影响异常继续往外走。这个机制跟except最大的区别是__exit__的职责定位在“清理”而不是“捕获”。你可以在__exit__里记录日志、统计指标、释放连接但不应该把业务上的continue、break、return逻辑塞进__exit__。2.3 如果__enter__本身抛了异常怎么办这点很容易被忽略。如果__enter__执行到一半抛异常比如资源获取失败解释器根本不会调用__exit__。逻辑上也讲得通资源都没有成功建立拿什么清理class FailEnter: def __enter__(self): raise RuntimeError(资源获取失败) def __exit__(self, exc_type, exc_val, exc_tb): print(这条不会打印) return False with FailEnter(): pass运行时只会看到RuntimeError看不到这条不会打印。如果你的资源类在__enter__里开了分步骤的资源比如先连数据库再建游标而建游标失败就需要在__enter__内部自己补一层try/except来回收前面已获得的资源。不要把“连数据库失败也要释放句柄”这件事寄希望于__exit__它不会被执行。3. 手写上下文管理器的两种姿势与选型逻辑3.1 用类实现的模板通用模板是class ManagedResource: def __init__(self, *args, **kwargs): # 别在这里做重量级初始化 self.args args def __enter__(self): # 在此建立资源、返回句柄 return self def __exit__(self, exc_type, exc_val, exc_tb): # 在此清理资源返回值控制异常是否被吞 return False类实现最清晰适合资源生命周期比较明确的场景。有一个细节__init__和__enter__的执行时机不同。with ManagedResource()时__init__在__enter__之前执行但有些代码喜欢在__enter__里才做真正的初始化这样对象可以在模块级别被创建真正“进入”时才占用系统资源。我在写连接池管理器时把这个分隔做得非常明确构造是轻量的进入with之后才真正创建底层套接字。3.2 用生成器实现的魔法contextlib.contextmanager装饰器提供了另一种思路把yield作为分界线yield之前是进入逻辑yield之后是退出逻辑from contextlib import contextmanager contextmanager def managed_file(path, moder): file_obj open(path, mode) try: yield file_obj finally: file_obj.close() with managed_file(/tmp/demo.txt, w) as f: f.write(hello)你觉得这段代码里yield暂停了函数其实它没有真正暂停很长的时间而是把执行权交给了with内部的业务代码。业务代码运行完无论是否异常finally都会触发文件一定会关闭。关键在于try/finally不能省如果漏掉finally业务代码抛异常后生成器无法继续执行关闭逻辑资源就泄漏了。3.3 类实现和生成器实现怎么选对比维度类实现contextmanager生成器实现可读性方法分块清晰顺序紧凑yield上下界限清晰处理复杂状态灵活可以用实例属性保存状态稍显笨拙依赖局部变量和闭包复用性可继承、可组件化适合一次性的工具场景异常精细处理可细粒度控制返回值主要靠try/except/finally代码量较多少我的经验是写通用组件、需要被多处继承的基类用类实现写某个模块内部的小工具比如临时改环境变量、计时器、临时切换工作目录用生成器实现代码更紧凑可读。4. contextlib里几个被低估的宝贝ExitStack、suppress、closing4.1 ExitStack动态管理一组上下文管理器ExitStack解决的是“数量不确定、执行顺序不确定”的清理问题。它像一个“清理栈”你可以往里面塞任意多个上下文管理器或回调函数等到with块结束时统一逆序清理。from contextlib import ExitStack def write_many_files(file_specs): with ExitStack() as stack: files [ stack.enter_context(open(path, mode)) for path, mode in file_specs ] for f in files: f.write(同一批数据\n)这段代码如果没有ExitStack要么写一堆嵌套的with要么用列表收集文件对象再手动逐一close。enter_context会在进入时逐步打开文件如果第三个文件打开失败前两个也会被自动关闭。这个特性在实现批量资源清理时价值极高。它还支持callbackwith ExitStack() as stack: stack.callback(print, 最后执行的清理动作) print(业务中)callback没有参数时会在退出时调用带参数就传递参数。它特别适合“需要把清理动作与业务代码解耦”的场景。4.2 suppress真的想忽略异常时的选择平时我们都强调别轻易吞异常但有些异常确实可以安全忽略比如删除一个可能不存在的临时文件。from contextlib import suppress import os with suppress(FileNotFoundError): os.remove(/tmp/not_exist.txt)这比try/except FileNotFoundError: pass少了两行缩进语义也更明确“这段代码里如果出现这种异常忽略它”。注意它只吞指定异常其他异常照常抛出不会像return True那样把一切异常都吞掉。4.3 closing为没有上下文协议的对象提供补丁有些老库只提供了close()方法没有实现__enter__/__exit__。这时用closing包一层from contextlib import closing import sqlite3 conn sqlite3.connect(:memory:) with closing(conn) as con: cur con.cursor() cur.execute(SELECT 1)它做的事情很简单在退出时调用传入对象的close()。注意它不会自动提交事务也不会自动回滚拿到closing包数据库连接时业务代码里的提交/回滚还是要自己控制。5. 实战案例串讲从文件锁到数据库事务再到临时环境变量5.1 线程锁的标准用法threading.Lock本身实现了上下文管理器协议所以可以这样写import threading lock threading.Lock() counter 0 def increment(): global counter with lock: temp counter temp 1 counter temp很多人会问“锁也要上下文管理器吗”答案是要。如果只用lock.acquire()然后release()一旦acquire和release之间抛异常锁就永远解不开后面所有线程都会卡死。with lock在退出时必然释放从源头拆掉了这种死锁隐患。5.2 数据库事务的提交与回滚封装数据库事务是上下文管理器另一个高频战场。以标准库sqlite3为例可以封装一个事务管理工具import sqlite3 from contextlib import contextmanager contextmanager def transaction(conn): try: yield conn conn.commit() except Exception: conn.rollback() raise conn sqlite3.connect(:memory:) conn.execute(CREATE TABLE t (id INTEGER PRIMARY KEY, name TEXT)) with transaction(conn) as con: con.execute(INSERT INTO t (name) VALUES (测试))关键在于yield之后的两条分支正常执行完则提交出现异常则回滚并且重新抛出。这里特意让异常继续往外传播调用方才能感知业务失败。我最开始写这个封装时在except里只回滚没有raise导致所有失败操作都变成了“看起来成功的假象”这个教训让我后来写所有事务封装都会检查异常是否继续上抛。5.3 临时切换环境变量在做参数化测试时经常需要临时修改环境变量测试完还要还原。这个需求用contextmanager封装非常舒服import os from contextlib import contextmanager contextmanager def set_env_var(key, value): old_value os.environ.get(key) os.environ[key] value try: yield finally: if old_value is None: os.environ.pop(key, None) else: os.environ[key] old_value with set_env_var(MY_FLAG, 1): print(os.environ.get(MY_FLAG))这种工具的特点是“业务代码只管用附近的环境在我进入前和退出后保持一致”。我在本地跑一些依赖环境变量切换的脚本时经常把多个工具函数串起来代码里完全看不到手动恢复环境变量的样板代码。6. 踩坑实录我在真实项目里遇到的五个致命陷阱6.1 自定义上下文管理器返回了错误的对象有个很隐蔽的坑__enter__没有返回值。class BadManager: def __enter__(self): # 忘了 return self pass def __exit__(self, *args): return False with BadManager() as data: print(data) # 拿到的是 None问题在于as绑定的值是__enter__的返回值不是BadManager()实例。如果__enter__不写返回值后面拿到的就是None调用方法直接AttributeError。排查这种问题不难但要靠经验意识到“可能不是业务代码错了而是上下文管理器没返回对象”。6.2 __exit__里做了太重的操作阻塞主流程有段时间我写了一个上报埋点数据的上下文管理器把__exit__里的日志上报做成了同步网络请求。结果线上一个高频接口每次结束都要等网络超时接口延迟从几十毫秒变成了十几秒。这个教训极其深刻上下文管理器的退出逻辑也会阻塞业务线程不要在__exit__里做慢的网络调用、大批量磁盘写入。如果确有必要把上报动作丢进异步任务队列或者用非阻塞方式触发不要堵在主流程里。6.3 多个with嵌套导致执行顺序和直觉相反with open(a.txt, w) as fa: with open(b.txt, w) as fb: print(写两个文件)执行顺序是先进入a.txt再进入b.txt退出时先退b.txt再退a.txt也就是后进先出。这个顺序跟嵌套try/finally完全一致。理解清楚这一点很重要尤其是多个连接有依赖关系时比如先建会话再基于会话建事务退出就应该先回滚/关闭事务再关闭会话。6.4 生成器式上下文管理器漏了try/finally用contextmanager时最常犯的错contextmanager def wrong_managed_file(path): f open(path, w) yield f f.close()业务代码如果抛异常程序会直接跳出该函数yield后面的关闭代码不再执行。必须用try/finally包住yield确保异常路径下也能关闭。这个坑跟文件泄漏直接相关我用调试工具统计过未关闭的文件句柄定位出来的答案往往是这里漏写了finally。6.5 异常追踪栈被截断、根因难查如果__exit__里捕获了异常但没有重新抛出只是打印日志外层就看不到完整链路。反过来suppress也会隐藏被吞异常。排查线上问题时我习惯先在上下文管理器里临时把exc_type、exc_val、exc_tb完整打印出来。确认问题根因前不要过早用suppress或return True否则出错时只能看到结果错误看不到过程。7. 异步上下文管理器async with 的协议和适用场景7.1 协议长什么样Python在异步编程里也提供了配套协议名称是__aenter__和__aexit__class AsyncResource: async def __aenter__(self): print(异步进入) return self async def __aexit__(self, exc_type, exc_val, exc_tb): print(异步退出) return False async def main(): async with AsyncResource() as res: print(异步业务)注意不要遗漏async关键字。async with天然适合资源获取/释放本身就是异步调用的场景比如异步HTTP会话、异步数据库连接池。7.2 一个异步连接池的封装思路假设你在用某个支持异步的HTTP客户端库每个请求都要建连接性能会很差。通常的做法是维护一个连接池而上下文管理器可以把“取连接”和“还连接”封装起来from contextlib import asynccontextmanager asynccontextmanager async def pooled_connection(pool): conn await pool.acquire() try: yield conn finally: await pool.release(conn)调用方不需要关心连接从哪来、用完怎么还只要写async with pooled_connection(pool) as conn: result await conn.request(/api/data)从代码结构上看这个模式和同步的生成器上下文管理器非常像只是函数变成async def装饰器变成asynccontextmanager。7.3 异步场景下的特殊注意事项异步上下文管理器尤其要小心一个问题不要在__aexit__里去同步阻塞等待某个耗时操作。异步代码里一行time.sleep(2)会卡住整个事件循环应该用await asyncio.sleep(2)。另外如果业务代码在async with内部抛异常而__aexit__又要做一次异步清理清理动作本身也可能抛异常。这时异常会被覆盖成清理异常原异常信息反而丢失。我见过这样的问题排查半天发现本来的业务异常被清理阶段的报错压下去了解决方法是把原异常挂到上下文注解里或者用contextlib提供的辅助工具做异常链改写。8. 我在实际使用中最后沉淀下来的几条经验上下文管理器越用越顺手之后写业务代码的底气会明显不一样。如果让我只保留三条经验传递出去核心是这几条。第一所有涉及“获取-使用-释放”的资源无论文件、锁、连接、临时状态第一反应都应该考虑用with封装而不是写裸的try/finally。这不是风格问题是正确性问题和可维护性问题。第二不要把业务控制流塞进上下文管理器。__exit__是清理逻辑的归宿不是业务判断的地方。让异常自然传播、让调用方决定怎么处理是上下文管理器最合理的使用方式。第三调试资源泄漏问题时先检查所有自定义上下文管理器是否覆盖了“异常路径”。我整理过一个自查清单__enter__失败时已经获取的部分资源有没有释放yield之后的清理有没有包在finally里__exit__返回值会不会无意中吞掉异常。上下文管理器最迷人的地方在于它把“资源的边界”变成了代码的结构边界让正确性不再依赖开发者的细心。理解了协议本身之后很多所谓的高级技巧其实都是这个协议的合理外推。希望这篇文章能帮你把这一块彻底打通下次写with的时候心里不再是“模板语法”而是清清楚楚地知道每一步在协议里如何工作。

相关新闻

Agent Skills实战:智能体技能体系设计与编排评估

Agent Skills实战:智能体技能体系设计与编排评估

从“会聊天”到“会办事”,Agent Skills 才是智能体的分水岭 做 Agent 开发这几年,我最大的感触是:模型本身的聪明程度确实重要,但真正让一个智能体从“什么都懂一点”变成“能把事办成”的,往往是它手里的那套技能——…

2026/10/15 9:34:02 阅读更多 →
未公开轻量引擎实战:50款游戏挑战Three.js

未公开轻量引擎实战:50款游戏挑战Three.js

1. 这个标题到底在聊什么 先把话说在前头:这个标题里说的“未公开的引擎”,不是指某个藏着掖着不给人看的商业闭源产品,而是指一个 还没有正式对外发布、只在内部小圈子流通的轻量级 Web 渲染引擎 。我前后花了大概三个月时间,用…

2026/10/15 9:59:49 阅读更多 →
人工智能本质与落地:破解误区,掌握AI应用与学习路线

人工智能本质与落地:破解误区,掌握AI应用与学习路线

这些年我经常被问到一句话:你真的了解人工智能吗?问的人有学生、有职场人,也有纯粹被新闻刷屏的普通用户。每次听到这个问题,我都得先反问一句:你说的“了解”,是指会聊几句聊天机器人、看过几篇行业报告&a…

2026/10/14 8:40:13 阅读更多 →

最新新闻

2025年中国供应链物流科技创新发展报告

2025年中国供应链物流科技创新发展报告

适应人群为供应链物流企业(如快递、仓储、跨境物流)高管、物流科技从业者(AI 算法 / 自动化设备研发岗)、制造 / 电商企业供应链负责人、物流政策制定者及供应链物流研究人员。主要内容围绕 2025 年中国供应链物流科技创新展开,核心包括全球技术趋势(聚焦生成式 AI、供应…

2026/10/16 3:05:00 阅读更多 →
EmbodiedSmith:利用仿真环境中的递归自我改进飞轮扩展具身数据

EmbodiedSmith:利用仿真环境中的递归自我改进飞轮扩展具身数据

26年10月来自港科大(广州)、小米机器人、东南大学、清华、浙大、西湖大学、共生知行公司(Symbiosis Robotics)、北大、西交大和北京智源的论文“EmbodiedSmith: Scaling Embodied Data through Recursive Self-Improvement Flywheel in Simulation”。 EmbodiedSmith 最有…

2026/10/16 3:05:00 阅读更多 →
2026企业降本新引擎:智能Agent落地场景与ROI重构实战

2026企业降本新引擎:智能Agent落地场景与ROI重构实战

聊个最近总被问到的话题:2026年企业降本还能怎么做?很多管理者发现,常规的流程优化和外包已经压不出太多水花了,于是把目光转向了智能Agent。但市场上叫Agent的产品一大堆,真到自己落地,就面临一个很现实的…

2026/10/16 3:05:00 阅读更多 →
现场总线风云录:从4-20mA到工业以太网的二十年

现场总线风云录:从4-20mA到工业以太网的二十年

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 3:05:00 阅读更多 →
Quantopian源码解析:量化教学方法论与zipline回测原理

Quantopian源码解析:量化教学方法论与zipline回测原理

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 3:05:00 阅读更多 →
MyBatis-Plus 实战指南:从基础 CRUD 到条件构造器与自定义 SQL(一)

MyBatis-Plus 实战指南:从基础 CRUD 到条件构造器与自定义 SQL(一)

目录 ​编辑 1. MyBatis-Plus 介绍 2. 快速上手 2.1 项目依赖与配置 Spring Boot 2 Spring Boot 3 Spring Boot 4 application.yml application.properties 2.2 实体类与 Mapper 2.4 基础 CRUD 3. 常见注解与日志 3.1 TableName 3.2 TableField 3.3 TableId 3.…

2026/10/16 3:03:59 阅读更多 →

日新闻

个人 AI Agent 平台接入 TaoToken:统一 Key 与 API 通道的配置大纲

个人 AI Agent 平台接入 TaoToken:统一 Key 与 API 通道的配置大纲

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 0:00:05 阅读更多 →
信号与系统频域分析作业解题指南

信号与系统频域分析作业解题指南

1. 作业背景与核心要求解析信号与系统作为电子信息类专业的核心基础课,其作业设计往往直接关联课程的核心知识框架。2026年春季第六次作业的布置,通常对应着课程进展到频域分析的关键阶段。根据常规教学进度推测,本次作业可能涉及以下核心内容…

2026/10/16 0:00:05 阅读更多 →
Django MVT实战:从零构建图书借阅系统,彻底搞懂Model-View-Template分层

Django MVT实战:从零构建图书借阅系统,彻底搞懂Model-View-Template分层

1. 从一个"四不像"的练手项目说起如果你已经看完了Django的官方教程前几章,跟着敲完了投票应用,大概会有一种"我好像会了,又好像什么都不会"的割裂感。教程里每个知识点都是拆开的——这一章讲模型,下一章讲视…

2026/10/16 0:00:05 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/15 16:18:54 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/15 19:41:51 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/15 13:48:15 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/15 0:14:36 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/14 22:45:47 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/15 9:49:14 阅读更多 →