码农高天
针对字典取值判断,开发者应摈弃单纯追逐纳秒级耗时的思维,仅在缺键确属反常错误时采用try捕获,默认值兜底首选get,不同复杂逻辑分支则应使用if处理。
AI-assisted summary of 【python】如何做字典的键值判断?if,get还是try?一个视频讲清楚! — original source. Check the original for full context.
Source published:
Key ideas
- 针对 Python 字典键值判断,逻辑等价的 if、get 与 try 三种写法在有键与无键时的性能表现截然相反:键存在时 try...except 最快,键缺失时 if 判断最快而 try 耗时暴增。 (0:00)
- 日常业务中 90% 以上的代码无需进行微观性能优化,字典键值判断的选型应优先遵循代码语义而非纳秒级速度差。 (2:30)
- 缺键仅需回退默认值时应首选 dict.get(),涉及不同复杂业务分支时用 if...else,而 try...except 仅应在缺键属于罕见异常时使用。 (2:30)
Detailed notes
## Background 该视频由 YouTube 频道“码农高天”于 2024 年 6 月 4 日发布。视频围绕 Python 中判断字典键是否存在时的常见方案展开,探讨在 if、get 与 try 三种逻辑等价的代码写法中,开发者在实际应用场景下应如何做出合理选择。
## TL;DR 视频对比了使用 if 条件判断、dict.get 方法以及 try...except 异常处理这三种检查字典键的常见方案。通过基准测试表明,当键存在时 try...except 凭借零开销异常机制速度最快,而当键缺失时 if 的性能则大幅领先于抛出异常的 try 结构。作者强调,绝大多数代码无需过度关注纳秒级微观性能,选型的核心在于代码语义:若仅需默认值兜底应首选 get;若两分支各自承担复杂业务逻辑应选用 if;唯有当键缺失本身属于罕见错误或真正异常时,才推荐使用 try...except。
## Atmosphere & style 整体基调严谨务实、抽丝剥茧,呈现出典型的资深工程师技术分享风格。高天以精简的代码和清晰的基准测试数据破除盲目追求性能的迷思,语调从容理性、层层递进。观看体验流畅透彻,既有对底层机制的敏锐洞察,又时刻立足于工程实践,将复杂的编程哲学收束为简单有力的决策准则。
## Bulletin - [00:00](https://www.youtube.com/watch?v=NjeiQY9aNW4&t=0s) 针对 Python 字典键值判断,逻辑等价的 if、get 与 try 三种写法在有键与无键时的性能表现截然相反:键存在时 try...except 最快,键缺失时 if 判断最快而 try 耗时暴增。 - [02:30](https://www.youtube.com/watch?v=NjeiQY9aNW4&t=150s) 日常业务中 90% 以上的代码无需进行微观性能优化,字典键值判断的选型应优先遵循代码语义而非纳秒级速度差。 - [02:30](https://www.youtube.com/watch?v=NjeiQY9aNW4&t=150s) 缺键仅需回退默认值时应首选 dict.get(),涉及不同复杂业务分支时用 if...else,而 try...except 仅应在缺键属于罕见异常时使用。
## Key takeaways - [00:00](https://www.youtube.com/watch?v=NjeiQY9aNW4&t=0s) 从字典读取键值主要有三种逻辑等价方案:if 条件判断、dict.get() 默认值取值,以及 try...except 异常捕获。 - [00:00](https://www.youtube.com/watch?v=NjeiQY9aNW4&t=0s) 使用 try...except 查键时必须显式指定 KeyError,捕获宽泛的 Exception 会掩盖变量类型不兼容等隐蔽 Bug。 - [00:00](https://www.youtube.com/watch?v=NjeiQY9aNW4&t=0s) 目标键存在时 try...except 性能最佳,得益于 Python 3.11 零开销异常机制,正常执行路径几乎无额外损耗。 - [00:00](https://www.youtube.com/watch?v=NjeiQY9aNW4&t=0s) 目标键不存在时 try...except 耗时大幅增加,主要瓶颈在于构造与抛出异常对象的开销极其高昂。 - [00:00](https://www.youtube.com/watch?v=NjeiQY9aNW4&t=0s) 键不存在时 if 比 dict.get 更快,因其直接走常量赋值分支,而 get 仍需完成函数调用与后续的默认值运算。 - [02:30](https://www.youtube.com/watch?v=NjeiQY9aNW4&t=150s) 实际开发中 90% 至 95% 的代码无需进行过度性能优化,不应仅凭基准测试微秒级的差异决定写法。 - [02:30](https://www.youtube.com/watch?v=NjeiQY9aNW4&t=150s) Python 社区虽然推崇 EAFP 原则,但绝不应滥用异常机制将其作为通用的业务分支控制手段。 - [02:30](https://www.youtube.com/watch?v=NjeiQY9aNW4&t=150s) Python 解释器优化正常路径而重罚异常抛出的底层设计,与 try...except 用于罕见异常的语义要求完美契合。 - [02:30](https://www.youtube.com/watch?v=NjeiQY9aNW4&t=150s) 当字典键缺失仅需一个回退默认值时,永远推荐使用 dict.get(),其语义最清晰且代码量最少。 - [02:30](https://www.youtube.com/watch?v=NjeiQY9aNW4&t=150s) 当键的存在与否都属于常规状态且需要执行截然不同的复杂业务流程时,应当使用 if...else 结构。 - [02:30](https://www.youtube.com/watch?v=NjeiQY9aNW4&t=150s) 选择 if 还是 try 的终极判断准则,在于自问 except 代码块中所处理的场景是否真的是一个意外异常。
## Detailed walkthrough ### [00:00](https://www.youtube.com/watch?v=NjeiQY9aNW4&t=0s) 三种键值判断方案与性能基准测试
字典(dictionary)是 Python 中最基础且最常用的数据结构之一。在日常编程中,当我们尝试从字典中读取某个键对应的值时,往往需要先确认该键是否存在,否则直接访问不存在的键会导致程序报错。码农高天举了一个典型的业务场景:若字典 `d` 中存在键 `'a'`,则令 `x = d['a'] + 1`;若不存在,则令 `x = 1`。针对这一需求,逻辑上等价的实现方式主要有三种:最直观的方式是使用 `if` 条件判断(即 `if 'a' in d:`),这种写法如同将口语需求直接逐字翻译成代码;第二种是使用字典自带的 `get` 方法,通过指定默认值,用一行代码代替原本四行的逻辑;第三种则是进阶学习后常接触到的 `try...except` 结构,将赋值操作直接放入 `try` 块中捕获异常。针对异常捕获,高天特别强调了一个编程好习惯:“这里一定要注意一下,大家在 except 后面一定要习惯去写他具体的那个 error,比如这里一定要写成 KeyError。如果你在这写一个很 general exception,你就会错过一些 bug。”例如,若键 `'a'` 对应的值是一个字符串,加法操作本应抛出类型错误,但若捕获了通用的 `Exception`,该 bug 就会被悄悄掩盖。
```mermaid flowchart TD accTitle: 三种字典键值处理方式对比 accDescr: 展示if判断、get方法与try-except捕获在有键与无键时的执行路径
subgraph IF_BRANCH["方式一:if 判断"] A{"'a' in d?"} -->|是| B["x = d['a'] + 1"] A -->|否| C["x = 1"] end
subgraph GET_BRANCH["方式二:get 方法"] D["x = d.get('a', 0) + 1"] end
subgraph TRY_BRANCH["方式三:try...except 结构"] E["try: x = d['a'] + 1"] -->|触发 KeyError| F["except KeyError: x = 1"] end ```
许多开发者在面对这三种功能等价的写法时,直觉会认为“哪个快就用哪个”。通过使用 `timeit` 进行性能基准测试,可以发现运行速度随键是否存在而发生戏剧性变化: - **当字典中包含目标键时**:`try...except` 速度最快,`get` 略快于 `if` 判断。这是因为 `try` 语句在设计上保证了正常运行时的极低开销;尤其是在 Python 3.11 引入零开销异常(Zero-cost exception handling)后,正常路径下几乎没有额外耗时。 - **当字典中不存在目标键时**:`if` 的速度最快,`get` 相对慢了一倍左右,而 `try...except` 则极慢。`try` 之所以变慢是因为构造和抛出异常对象的开销极大;而测试中 `if` 显著快于 `get`,也是因为 `if` 的 `else` 分支直接给 `x` 赋常数 `1`,而 `get` 依然需要执行函数调用返回默认值 `0` 再完成一次加法。
[02:30](https://www.youtube.com/watch?v=NjeiQY9aNW4&t=150s) 性能之外:根据语义选择最恰当的代码表达
尽管基准测试能揭示性能差距,但码农高天明确指出,在实际工程开发中过分纠结微观性能往往是本末倒置的:“然而在我们的实际编程中,性能的重要性往往是被高估的,你写的 90% 甚至 95% 以上的代码是不需要做专门的性能优化的。”在选择字典判断方式时,开发者应当优先考虑语言特性所对应的语义,而非单纯追逐纳秒级的性能差异。Python 社区虽然推崇“请求宽恕好过事前许可”(EAFP: Ask for forgiveness, not permission)的哲学,但这并不意味着应该无节制地滥用 `try...except`。`try...except` 本身具有强烈的“异常”语义,`except` 块应当且只能用于处理真正的特殊情况或错误边界。换言之,该模式只适合“开发者笃定该键必然存在于字典中,若不在则代表出现了罕见故障或异常”的语境。巧合的是,这种语义恰恰与 Python 底层的性能优化方向一致——即优化正常运行路径,而在真正抛出异常时承受较高惩罚。
```mermaid flowchart TD accTitle: 字典查键方案决策树 accDescr: 根据代码语义与分支逻辑复杂度选择if、get还是try
Start{"字典缺少该键是异常情况吗?"} Start -->|"是(属于罕见错误/Bug)"| UseTry["使用 try...except KeyError\n语义精准,常态性能最高"] Start -->|"否(属于常规业务场景)"| CheckDefault{"是否仅需一个默认值替代?"}
CheckDefault -->|"是(仅做值回退)"| UseGet["永远使用 dict.get(key, default)\n代码量最少,语义最清晰"] CheckDefault -->|"否(存在/不存在需完全不同的业务处理)"| UseIf["使用 if key in d:... else:...\n逻辑分支清晰,便于扩展复杂操作"] ```
根据具体语义,高天总结了三大使用场景的最佳实践准则: - **使用 `try...except`**:高天给出明确建议:“所以我个人的推荐是只有当字典里没有这个 key 是一个奇怪的事情的时候,再用 try except。”若键的缺失本就是程序预料之内的常见分支,则不应滥用异常机制。 - **使用 `dict.get()`**:“如果没有这个 key 的时候,你只是需要一个默认值顶替它,永远用 get,因为 get 是语义最清晰最容易读的,同时它的代码量也最少。”这是兜底填充场景下的首选。 - **使用 `if...else`**:当键的存在与否都属于系统的正常状态,且两个分支需要执行截然不同的复杂业务逻辑时(例如存在时执行累加,不存在时执行重新计算字典长度并赋值等无默认值可替代的操作),`get` 无法胜任,而 `try...except` 无论在语义还是性能上都不合适,此时必须使用 `if...else`。
面对很多既能写成 `if...else` 也能写成 `try...except` 的场景,最终的衡量标尺十分简单:“我们的大原则永远是问问自己,except block 里面那个情况到底是不是真的 exception。”
## Notable quotes > [02:30](https://www.youtube.com/watch?v=NjeiQY9aNW4&t=150s) 在我们的实际编程中,性能的重要性往往是被高估的,你写的 90% 甚至 95% 以上的代码是不需要做专门的性能优化的。 — 码农高天
> [02:30](https://www.youtube.com/watch?v=NjeiQY9aNW4&t=150s) 我们的大原则永远是问问自己,except block 里面那个情况到底是不是真的 exception。 — 码农高天
> [02:30](https://www.youtube.com/watch?v=NjeiQY9aNW4&t=150s) 如果没有这个 key 的时候,你只是需要一个默认值顶替它,永远用 get,因为 get 是语义最清晰最容易读的,同时它的代码量也最少。 — 码农高天
## Mentioned 码农高天, Python, timeit
## Video description 在判断一个key是否在字典里的时候,我们有几个逻辑上等价的方法,分别是用if,get和try。实际应用中我们应该怎么去选择使用呢?
[Source](https://www.youtube.com/watch?v=NjeiQY9aNW4)