Python字典安全取值与返回设计:告别KeyError的工程实践
做数据处理脚本的时候十个报错里至少有五个是 KeyError。很多人刚开始接触 Python觉得字典无非就是d[key]取个值、d.items()遍历一下没啥好学的。结果一写真实业务代码就发现今天这个键没传、明天那个值是 None、后天嵌套结构里某个层级压根不存在代码越写越像防御性编程大赛满屏都是if key in d。这篇就把我日常写 Python 索引、字典查询和函数返回字典内容时的那套习惯摊开讲清楚什么时候用[]、什么时候用get()嵌套数据怎么安全读取怎么把查询结果组织成字典返回以及怎么避免返回的字典在调用端失控。适合刚入门不久、想把自己脚本写得更稳的读者也适合写过一阵但总在字典取值上摔跟头的老哥们。1. 字典取值的第一道分水岭[]与get()的边界在哪里1.1 中括号索引的毒与药写法最顺报错也最狠字典和列表、元祖这类序列最大的不同在于它不是靠连续的位置编号定位而是靠键key到值value的映射关系来查询。你可以把字典想象成一个带索引标签的抽屉柜标签是键抽屉里的东西是值。d[key]这个写法本质上就是在问这个标签对应的抽屉里有没有东西有就给你没有直接抛 KeyError。这个设计很合理但也正是它坑人最多的地方。d[name]写起来干净利落可一旦这个键不存在程序当场就断。我见过很多新手业务代码在外部接口返回的数据上直接用[]取字段接口一改返回结构整个脚本瞬间崩在生产环境里。所以我的经验是只有两种情况可以放心用中括号索引。第一种是你非常确定键一定存在比如刚构造好的字典、用dict(zip(keys, values))拼出来的结构、或者刚从json.loads()解析出来且你明确知道上游字段不会缺。第二种是故意让它报错——快速失败fail fast比后续拿着 None 一路算下去要容易排查得多。# 这种场景用 [] 完全没问题键是刚写进去的 user {name: 张三, age: 28} print(user[name]) # 张三 # 这种场景就纯属给自己埋雷外部数据结构你根本控制不了 def get_user_name(raw): return raw[user][name] # 一旦缺键KeyError 直接炸我实测里最常见的翻车姿势是在循环里对一组字典统一取某个字段其中一两条记录缺这个字段。程序跑了大半突然崩掉前面处理的结果全要重来。这种时候中括号就不是工具是地雷。1.2 get()的默认值技巧把可能没有变成提前兜底dict.get(key, default)是处理可能没有的标准答案。它做的事情很朴素键存在就返回值不存在就返回你给的默认值绝不报错。很多人知道这个方法但用得很糙——要么所有地方全换成get()要么默认值永远传None。这两种做法其实都没吃到get()的完整红利。先说第一个极端所有地方全部get()。这会让代码丧失自描述能力。data[id]一眼看过去就是一个必须存在的字段data.get(id)则意味着有没有都行。如果一段代码逻辑上依赖某个键必须存在你却用get()把缺失问题悄悄吞掉后面拿到的可能是 None接着 None 参与计算报错的地方会从取值处漂移到遥远的运算处排查成本高好几倍。正确的姿势是逻辑上必须有的字段用[]可选字段才用get()且默认值要给得有业务含义。第二个极端默认值永远传None。默认值的作用不只是别报错更是给后续逻辑一个合理的起点。比如统计用户积分的时候用户可能没有 score 这个字段默认给 0 就比给 None 合理得多user_a {name: Bob, score: 85} user_b {name: Alice} # 没有 score # 默认值给 None后面就得额外判断 score_a user_a.get(score, None) score_b user_b.get(score, None) # 默认值给 0直接就能参与加法 score_a user_a.get(score, 0) score_b user_b.get(score, 0) total score_a score_b # 不用再判 None1.3 setdefault和defaultdict初始化场景下的两个加速器查询字典之外有一类特别常见的需求是按某种规则向字典里填充数据统计词频、按类别分组、建索引。这类操作的核心痛点是键可能不存在第一次写入时需要先初始化。如果每次都用if key not in d: d[key] []这种写法代码会变得又长又容易漏。dict.setdefault(key, default)的做法是键不存在就把默认值放进去并返回键存在就直接返回已有值。以分组统计为例words [apple, banana, apple, orange, banana, apple] # 用 setdefault 统计每个词出现次数 counter {} for w in words: counter[w] counter.setdefault(w, 0) 1 # 按首字母分组 groups {} for w in words: groups.setdefault(w[0], []).append(w)第一段代码里setdefault(w, 0)保证了 counter 里一定有当前单词这个键然后加一覆盖回去。第二段代码先把键初始化成空列表再 append每一步都不会出现 KeyError。collections.defaultdict是另一个更省事的方案它要的是工厂函数而不是具体默认值访问不存在的键时自动调用工厂函数生成初始值并写入。上面第二段可以直接写成from collections import defaultdict groups defaultdict(list) for w in words: groups[w[0]].append(w)看着更简洁但有两点要注意。第一defaultdict会让检查键是否存在变得不那么直观因为访问不存在的键会在无形中创建一个键如果你只是想做判断用普通dict判断更安全。第二序列化或者给别人看数据结构的时候默认字典和普通字典在 JSON 导出前的长相不一样需要先转成普通 dict 再转 JSON否则可能带着默认值一起序列化出去。实际选哪个我倾向于逻辑比较线性、不希望隐式创建键的地方用 setdefault批量填充数据的场景用 defaultdict。2. 嵌套字典查询把多层结构取值的手工活变成一次性工具2.1 实际场景还原从接口返回的JSON里取一个深字段现实世界的数据很少是单层字典。接口返回的 JSON 动辄三四层嵌套比如一个订单详情长这样order { status: paid, customer: { profile: { address: { city: 上海 } } }, items: [ {sku: A001, price: 199}, {sku: A002, price: 399} ] }如果我只想取city这个字段新手第一反应是顺着键一层层取city order[customer][profile][address][city]这段代码单独看没问题但把它放进真实项目里就有隐患customer可能没有profile可能没有address也可能没有。任何一层缺失程序就炸。为了稳住很多人会写成下面这种连环 ifif customer in order: if profile in order[customer]: if address in order[customer][profile]: city order[customer][profile][address].get(city, 未知)这么写能跑但四层缩进之后代码已经没法看了再加一层字段维护成本直线上升。这种代码我在接手别人的脚本时见过多次每回都想原地重构。2.2 层层保护式取值的代码膨胀问题嵌套取值防御过度的另一个副作用是每多一层嵌套你就得多写一遍判断多引入一个临时变量或者多一层缩进而且这种写法的错误非常隐蔽——比如某层写错了键名而恰好这一层有别的键能通过判断最后取值取错了还没人发现。我自己以前也写过类似代码直到有一天要处理七层嵌套的配置结构实在写不下去了才开始封装工具函数。封装的思路很简单把一层层往下找这件事交给函数路径用可迭代对象或者带分隔符的字符串表达每到一个节点都判断一下类型是否还是字典、键是否存在不存在就返回默认值。def safe_get(data, path, defaultNone): if isinstance(path, str): path path.split(.) cur data for key in path: if not isinstance(cur, dict) or key not in cur: return default cur cur[key] return cur用起来是这样city safe_get(order, [customer, profile, address, city], 未知) # 也可以直接用字符串路径 city safe_get(order, customer.profile.address.city, 未知)函数内部每走一层都会检查isinstance(cur, dict)这比直接取cur[key]安全得多即便中间某层拿到的是空字符串、None 或者其他类型也不会直接报错而是干净地落到默认值上。2.3 自己封装safe_get路径式读取嵌套字典的使用心法封装工具函数时有个细节很多人会忽略路径要不要用字符串加点的形式。字符串路径写起来简洁但它有个隐藏假设——所有键名里不能含有英文句号。真实数据里键名带点号虽然少见但不是没有。比如某个上游接口的字段叫user.name你写user.name这条路径函数会把它拆成两层user和name结果完全错误。我的处理办法是调用方如果传字符串我按点号拆如果传元组或列表我就把它当完整路径不再拆。这样既能保留字符串路径的方便又给了调用方一个绕开歧义的出口。上面的函数已经有这个能力——isinstance(path, str)时才拆传列表就直接用确实是这样。实际项目里我更推荐统一用元组传路径因为从调用处一眼就能看出它要下探几层、键名是什么重构时也好搜索替换。再补充一个性能侧的建议。safe_get这种逐层遍历的实现在数据量不大的场景下完全够用但如果在一个字典列表里高频调用比如百万条记录每条都要取嵌套字段那函数调用开销就该被认真对待。这种情况下有两种优化思路一是把路径预先拆成元组缓存起来避免重复做字符串 split二是能合并的查询就不要分多次safe_get一次性把需要的中间节点取出来再复用。实测下来批量处理十万级数据时这些优化能让整体耗时降低三成左右。嵌套取值的本质是把你对这个数据的形状一定有这些东西的假设变成显式代码。safe_get的默认值参数就是把这些假设的兜底方案集中到一个地方让主逻辑保持清爽。3. 查询不只是取值倒查、筛选、裁剪字典的输出姿势3.1 根据值反查键一对一用推导式一对多用defaultdict字典的正向查询是根据键找值但业务里经常遇到反过来的时候给你一个值你想知道它对应哪个键。比如一个省市映射表你只知道城市名想知道它的省份编码。最朴素的做法是遍历整个字典city_to_province {杭州: 浙江, 宁波: 浙江, 南京: 江苏} def find_key(d, target): for k, v in d.items(): if v target: return k return None这个写法逻辑没问题但要说更 Pythonic我推荐直接用字典推导式把原字典反转成新字典。一对一反转的场景一行代码就够province_to_city {v: k for k, v in city_to_province.items()}但这里有个大坑如果原字典里有重复值反转后后面出现的键会覆盖前面出现的键。上面的例子 浙江 对应的键只剩 宁波杭州 就丢了。要保留一对多关系就得用 defaultdict 收集from collections import defaultdict province_to_cities defaultdict(list) for city, province in city_to_province.items(): province_to_cities[province].append(city)反转后的结果长这样{浙江: [杭州, 宁波], 江苏: [南京]}。这种倒查需求在数据清洗里太常见了比如把标签名映射到标签 ID再把用户打过的标签列表翻译回可读名字。3.2 条件筛选生成子字典过滤出真正需要的字段查询内容返回字典内容的另一种经典形态是过滤出符合条件的键值对。最常见的场景是从一个大字典里挑出某些前缀的键、数值超过阈值的内容、或者剔除掉值为 None 的脏数据。用字典推导式可以直接生成一个崭新的子字典raw {a: 1, b: None, c: 3, d: , e: 0} # 剔除值为 None 或空串的键 cleaned {k: v for k, v in raw.items() if v is not None and v ! } # 筛选数值大于等于 1 的键值对 filtered {k: v for k, v in raw.items() if isinstance(v, (int, float)) and v 1}筛选条件本身如果比较复杂Python 3.8 之后引入的海象表达式walrus operator可以帮上忙避免在条件里重复计算同一个值。比如从一组用户字典里挑出分数大于 60 的同时只关心分数本身users { u1: {name: A, score: 72}, u2: {name: B, score: 55}, u3: {name: C, score: 88} } passed {uid: info for uid, info in users.items() if (score : info.get(score, 0)) 60}score在条件里被算出来一次并复用后面如果有对分数的二次处理也不用再取一遍。这里面有个容易被忽略的设计思想字典推导式的产出可以不是原字典本身它可以是一个投影。比如只保留每个内层字典的某几个字段整体结构还是一个字典这在写对外输出的接口时很常用。3.3 批量提取与字段补齐让返回的字典保持统一结构第三种查询对象是一批键都要查出来然后统一返回它对应的是另一个非常常见的需求——给外部调用方组装一个固定结构的字典。比如一个学生数据源里每份原始记录字段参差不齐但下游接口要求必须都包含姓名、年龄、成绩、备注四个字段缺的用默认值补。这时候可以写一个提取加补齐的小工具def build_student_record(record): return { name: record.get(name, 未知), age: record.get(age, 0), score: record.get(score, 0), remark: record.get(remark, ) } records [ {name: Tom, age: 18, score: 90}, {name: Jerry, age: 19, score: 82, remark: 进步快}, {name: Mickey, score: 75} ] normalized [build_student_record(r) for r in records]这样下游无论拿到哪条记录字段结构完全一致处理代码可以放心地操作remark这类键而不用再反复判空。这套做法在对接第三方系统时价值极大——上游数据结构不稳定你把字段是否存在这个不确定性在入口处全部消化掉后端的业务逻辑就可以假定数据完全规整。实际工作中我还常用一个技巧当需要从一个嵌套结构里提取若干字段时把safe_get和字段补齐结合起来。先定义一张字段映射表然后用一个字典推导式一次性生成输出结构field_paths { city: (customer, profile, address, city), phone: (customer, phone), total_price: (items_total,), } def extract_snapshot(order, paths): return {out_key: safe_get(order, path, 未知) for out_key, path in paths.items()}这种写法把取哪些字段和怎么取彻底解耦字段清单调整时只改field_paths这一处查询逻辑完全不用动。4. 返回字典内容的函数设计从返回一堆变量到返回一个结构4.1 返回值设计的痛点多返回值在调用端容易错位单独看字典的查、取、筛都不难难的是把它们组织进一个系统的函数返回设计里。写过一定规模项目的人应该都经历过这个痛点函数返回多个值调用端按位置拆包。当时只有两三个返回值还能记住顺序等函数越改越多、返回值膨胀到五六个调用端就变成了一场灾难。def analyze_record(record): ... return name, age, score, remark, is_pass # 位置顺序全靠记忆这时候只要有一次调用没按顺序接收变量就错位了。我在排查别人代码的时候见过真实案例因为返回值位置搞反把is_pass布尔值当成分数去比较大小日志里还看不出任何报错逻辑就是不对。这就是多返回值的典型隐患——Python 不会帮你做任何结构校验。4.2 用字典聚合结果状态码、消息、数据放一起把多个返回值打包成一个字典返回是解决上面问题最简单直接的办法。字典里的键名自带语义调用端按键取数据顺序无关缺哪个键也能立刻看出来。最常见的模式是状态码 信息 业务数据三段式def query_user(uid): # 模拟查询过程 if uid not in user_db: return { success: False, code: 404, message: 用户不存在, data: None } user user_db[uid] return { success: True, code: 0, message: ok, data: { name: user[name], age: user[age], score: user.get(score, 0), tags: user.get(tags, []) } }调用端可以这样消费result query_user(u1) if result[success]: print(result[data][name]) else: print(result[code], result[message])这种返回风格在脚本化的数据处理里可能有点重但在稍微成体系的代码里它带来的收益非常明显调用方和提供方之间形成了一种轻量约定字段名本身就是文档查错时也能一眼定位。很多原生的返回字典需求其实都是在这种模式上演化出来的——查询一批数据把每个数据项查到的结果、缺了什么字段、用了什么默认值全部写进一个字典里返回上游拿到的就是一个完整的查询快照而不是单个值。4.3 TypedDict和dataclass给返回字典加上约束裸字典虽好但有一个天生弱点键名写错时没有任何提示。如果调用方把success拼成seccess运行时只会拿到 KeyError而且得跑到实际执行那一刻才能发现。Python 3.8 起提供的TypedDict就是用来给字典加结构约束的from typing import TypedDict, Dict, Optional class QueryResult(TypedDict): success: bool code: int message: str data: Optional[Dict[str, object]] def query_user(uid: int) - QueryResult: ...加上这个类型标注函数返回字典的形状就具象化了。配合类型检查工具写错键名、漏写字段都能在开发阶段被拦下来。虽然这不影响运行时行为但对多人协作的项目来说类型提示本身就是一套廉价的文档。如果你想更进一步让返回的结构连怎么构造都规范化可以用数据类dataclass定义一个结果容器需要转成字典时再统一转from dataclasses import dataclass, asdict, field dataclass class QueryResult: success: bool code: int message: str data: dict field(default_factorydict) def to_dict(self): return asdict(self)数据类的优势在于字段可以在构造阶段做默认值、初值校验转字典asdict一步到位。这适合对返回字典内容有一套固定协议、并且多处复用的项目如果只是单个脚本里的临时返回裸字典加 TypedDict 已经绰绰有余。选哪种方案我个人的判断依据很简单临时脚本、一次性数据处理裸字典 get() 足够要做成模块给别人用至少 TypedDict要维护的代码规模再上一档就上 dataclass把返回结构的生命周期管理起来。回到标题本身——索引、字典灵活使用、查询内容、返回字典内容这几件事其实是一条线先用合理方式从字典里安全取值再做筛选、倒查、裁剪最后把结果组织成结构化的字典返回。真实代码里这几步经常发生在同一次数据清洗任务里从嵌套 JSON 安全取值过滤掉脏数据补齐默认字段攒成干净字典供下一步使用。把这套动作练顺手之后处理结构化数据的效率提升是肉眼可见的。最后分享一个我自己的习惯凡是涉及外部输入接口返回、配置文件、用户传参的字典统一在入口处用一套 safe_get 字段补齐的机制做一次格式化内部代码绝不再碰原始结构。这样字典查询的所有防御逻辑都收敛在一个文件里其他地方写起来干净利落排查问题也有一个明确的起点。这套方法论不复杂但它是我踩了两年 KeyError 的坑之后才总结出来的属于那种早知道就少熬夜的经验。

相关新闻

Oracle 11g RAC 异机恢复实战(漫画版)

Oracle 11g RAC 异机恢复实战(漫画版)

前言 节后刚到公司,业务那边就来了一个需求:有一套测试库的数据太老了,需要从生产库恢复一份数据过去。 生产库和测试库都是 Oracle 11g RAC,准备使用 RMAN DUPLICATE 完成恢复。原本以为很简单,就是清理旧库、恢复数据…

2026/10/14 22:08:28 阅读更多 →
边缘计算与云端协同实战:降低延迟、优化带宽的架构设计与运维经验

边缘计算与云端协同实战:降低延迟、优化带宽的架构设计与运维经验

搞边缘计算的人,这两年应该都有一个明显的体感:单纯把数据一股脑推到云端的做法,越来越扛不住业务需求了。数据量越来越大、实时性要求越来越高、带宽成本逐年上涨,你要是还按老思路去堆节点、扩带宽,运维成本和体验只…

2026/10/14 22:08:28 阅读更多 →
Python绘制3D球体网格散点图:球面数据点生成与可视化实战

Python绘制3D球体网格散点图:球面数据点生成与可视化实战

在开始之前,先说句实话:网上关于“Python 绘制 3D 球体”的教程,十个里有九个是在教你画一个实心球,再不然就是画一个线框球。但你真到了做项目的时候,往往会发现这些花架子根本不顶用。我这次要聊的,是“绘…

2026/10/14 22:08:28 阅读更多 →

最新新闻

Vue工程实践进阶:组件通信、组合式函数与性能优化全解析

Vue工程实践进阶:组件通信、组合式函数与性能优化全解析

Vue 笔记写到第七篇,这次不聊基础语法,也不画响应式原理的图解,重点全部放在工程实践里真正卡过人的地方:组件通信方案怎么选、组合式函数怎么拆才不会拆出一堆烂代码、路由守卫的执行顺序为什么老和预期不一样、Pinia 到底管什么…

2026/10/14 22:56:44 阅读更多 →
AI招聘实战:半自动方案如何避免账号风控与提升回复率

AI招聘实战:半自动方案如何避免账号风控与提升回复率

1. 为什么“AI做招聘”这件事,值得认真聊一聊招聘这件事,表面上看是“发岗位、收简历、约面试”三步走,但真正干过的人都知道,它本质上是一个高频沟通、强情绪、重信任的活儿。候选人投完简历之后,心里其实一直在打鼓&…

2026/10/14 22:56:44 阅读更多 →
冷热电联供系统多目标优化调度:MOPSO算法Matlab实现与Pareto前沿分析

冷热电联供系统多目标优化调度:MOPSO算法Matlab实现与Pareto前沿分析

做综合能源系统运行优化这项工作,我接触最多的一条技术线就是冷热电联供(CCHP)。它把天然气发电后的余热回收起来,再通过吸收式制冷和热交换把冷、热、电一块供出去,能量梯级利用效率比单纯发电高不少。但真正落地的时…

2026/10/14 22:56:44 阅读更多 →
AutoTool 实战:如何省下 30% 的 Agent 推理成本

AutoTool 实战:如何省下 30% 的 Agent 推理成本

1. 从 Jev 爆火说起:Agent 效率焦虑到底卡在哪Jev 这类通用 Agent 框架火起来之后,我身边不少做应用落地的朋友都经历了同一个心路历程:Demo 阶段惊艳,接入真实业务之后开始肉疼。疼的不是模型能力不够,而是推理成本和…

2026/10/14 22:56:44 阅读更多 →
2026专科生降AI率工具测评:九款实测与选型指南

2026专科生降AI率工具测评:九款实测与选型指南

每年到了毕业设计季,我都会收到不少专科院校同学的私信,问“我写的文章老是被标记为疑似AI生成,该怎么办”“有没有靠谱的降AI率工具推荐”。我不是学校老师,也不是什么平台官方,就是纯粹在内容创作和论文辅导这行泡了…

2026/10/14 22:56:44 阅读更多 →
原生JS组态软件实战:无后台监控界面的数据格式与架构设计

原生JS组态软件实战:无后台监控界面的数据格式与架构设计

简介:一份基于原生JavaScript实现的组态软件源码包,面向Web前端与工业自动化开发者,用于在无后台依赖环境中通过拖拽方式快速构建电力一次接线图、工业流程图等图形界面。压缩包共1013个文件,约14.4MB,以svg、png、gif…

2026/10/14 22:55:37 阅读更多 →

日新闻

AnyPS5通用适配层设计:兼容性与性能的工程取舍

AnyPS5通用适配层设计:兼容性与性能的工程取舍

1. 从"AnyPS5"这个名字说起:它到底想解决什么问题第一次看到"AnyPS5"这个标题,我脑子里冒出来的第一个念头是:这大概率是一个围绕"跨平台运行"或者"通用适配"做文章的项目。名字里的"Any"…

2026/10/14 0:00:41 阅读更多 →
Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

1. 项目概述:Flutter跨平台二手交易应用开发作为一名长期从事移动应用开发的工程师,我最近完成了一个基于Flutter框架的二手物品交易平台项目。这个项目最吸引我的地方在于它完美结合了Flutter的跨平台优势与HarmonyOS的生态特性,实现了"…

2026/10/14 0:00:41 阅读更多 →
DFS算法解决飞机降落调度问题实战

DFS算法解决飞机降落调度问题实战

1. 项目背景与核心思路飞机降落问题是一个经典的调度优化场景,也是算法竞赛和面试中的高频题目。这个题目要求我们合理安排多架飞机的降落顺序和时间,确保所有飞机都能安全着陆而不发生冲突。在实际航空管制中,这类问题直接关系到飞行安全和机…

2026/10/14 0:00:41 阅读更多 →

周新闻

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

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

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

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

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

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

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

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

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

2026/10/14 3:29:58 阅读更多 →

月新闻

我发现了一个新思路:用 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/13 22:01:27 阅读更多 →
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/14 7:53:34 阅读更多 →