# Python 复习训练册 · CPython 3.14.7

> 配套主手册：[`Python_碎片化复习手册_CPython_3.14.7.md`](Python_碎片化复习手册_CPython_3.14.7.md)  
> 训练职责：**变体 / Bug / 面试 / 专题收口**。基础定义与普通主动回忆留在主手册，不在这里维护第二套正文。

## 使用规则

1. 先在主手册把对应卡至少学到 M1。
2. **变体题**训练 M2：改变条件后预测行为。
3. **真实开发 Bug**训练 M3：从症状定位机制并给修复方向。
4. **面试追问**训练 M3～M4：解释边界、反例和工程权衡。
5. 答错时不要直接背答案；先根据调度 metadata 的 `rollback` 回退前置卡。

## 专题路径收口题

专题路径读完后，不要立刻换下一条。先合上正文，回答下面对应的一题。这里故意不直接给完整答案；答不稳时按“参考卡”回查，而不是从头重读。

| 路径 | 收口题 | 参考卡 |
|---|---|---|
| 对象模型 | `a = [[1]]; b = a.copy(); b[0].append(2)` 后，画出 `a`、`b`、外层 list、内层 list 的对象关系图。 | [6](Python_碎片化复习手册_CPython_3.14.7.md#card-006)、[9](Python_碎片化复习手册_CPython_3.14.7.md#card-009) |
| 函数参数 | 一个函数先 `items.append(1)`，再 `items = []`。调用方最后观察到什么？为什么两个语句影响不同？ | [13](Python_碎片化复习手册_CPython_3.14.7.md#card-013) |
| 作用域 / 闭包 | 解释为什么 `lambda: i` 在循环结束后通常都得到最终 `i`，以及 `lambda i=i: i` 为什么能修复。 | [22](Python_碎片化复习手册_CPython_3.14.7.md#card-022)、[25](Python_碎片化复习手册_CPython_3.14.7.md#card-025) |
| 装饰器 | 不使用 `@` 语法，把一个带参数装饰器的绑定过程完整写出来，并指出闭包保存了哪些状态。 | [24](Python_碎片化复习手册_CPython_3.14.7.md#card-024)、[26](Python_碎片化复习手册_CPython_3.14.7.md#card-026)、[27](Python_碎片化复习手册_CPython_3.14.7.md#card-027) |
| 容器 / 复杂度 | 你需要对 10 万条记录按 id 做 10 万次 membership/query。说明何时应从 list 转成 dict/set，以及构建成本为什么值得。 | [34](Python_碎片化复习手册_CPython_3.14.7.md#card-034)、[38](Python_碎片化复习手册_CPython_3.14.7.md#card-038)、[166](Python_碎片化复习手册_CPython_3.14.7.md#card-166) |
| 迭代 / 生成器 | 为什么 `list(generator)` 后再次 `list(generator)` 常得到空列表？如果需要两次完整遍历，设计上有哪些选择？ | [52](Python_碎片化复习手册_CPython_3.14.7.md#card-052)、[54](Python_碎片化复习手册_CPython_3.14.7.md#card-054) |
| OOP / Data Model | 解释 `obj.method` 为什么能自动绑定 `self`，并说明实例属性、data descriptor、non-data descriptor 的查找优先级。 | [58](Python_碎片化复习手册_CPython_3.14.7.md#card-058)、[65](Python_碎片化复习手册_CPython_3.14.7.md#card-065)、[66](Python_碎片化复习手册_CPython_3.14.7.md#card-066) |
| Import | A import B，B 又从 A 导入一个尚未定义的名字。用 `sys.modules` 和模块初始化顺序解释失败。 | [79](Python_碎片化复习手册_CPython_3.14.7.md#card-079)、[80](Python_碎片化复习手册_CPython_3.14.7.md#card-080) |
| Typing | 为什么 `def f(x: int): ...` 仍可在运行时收到字符串？那静态类型检查到底替你解决什么问题？ | [82](Python_碎片化复习手册_CPython_3.14.7.md#card-082) |
| Thread / GIL | 一个“先读余额、判断、再扣款”的多步操作，在传统 GIL CPython 下为什么仍需要同步？free-threaded 下又有什么变化？ | [110](Python_碎片化复习手册_CPython_3.14.7.md#card-110)、[113](Python_碎片化复习手册_CPython_3.14.7.md#card-113)、[114](Python_碎片化复习手册_CPython_3.14.7.md#card-114) |
| Asyncio | 一个 async handler 内调用 `time.sleep(5)`。从 event loop 角度解释其他请求为什么也会被拖住，并给出两个修复方向。 | [115](Python_碎片化复习手册_CPython_3.14.7.md#card-115)、[119](Python_碎片化复习手册_CPython_3.14.7.md#card-119) |
| GC / 生命周期 | 画出两个对象互相引用、外部名字都被删除后的对象图，并解释引用计数和 cyclic GC 各负责什么。 | [124](Python_碎片化复习手册_CPython_3.14.7.md#card-124)、[125](Python_碎片化复习手册_CPython_3.14.7.md#card-125) |
| subprocess / 安全 | 为什么 `shell=True` + 用户字符串是危险组合？仅改成 argv list 后还剩哪些输入安全问题？ | [144](Python_碎片化复习手册_CPython_3.14.7.md#card-144)、[182](Python_碎片化复习手册_CPython_3.14.7.md#card-182) |
| 数据库 | 一个请求更新三张表，第二张成功后第三张失败。说明 transaction 边界、rollback 和 connection pool 分别解决什么问题。 | [150](Python_碎片化复习手册_CPython_3.14.7.md#card-150)、[151](Python_碎片化复习手册_CPython_3.14.7.md#card-151) |
| 测试 / Mock | 被测模块 `from client import send` 后调用 `send()`。你 patch 哪个名字？再说明 Stub 与 Mock 的区别。 | [158](Python_碎片化复习手册_CPython_3.14.7.md#card-158)、[159](Python_碎片化复习手册_CPython_3.14.7.md#card-159) |
| 性能 | 一个接口慢 800ms，profiling 发现 Python 计算只占 20ms，却执行了 101 次 SQL。你先优化哪里？为什么？ | [167](Python_碎片化复习手册_CPython_3.14.7.md#card-167)、[170](Python_碎片化复习手册_CPython_3.14.7.md#card-170) |
| Packaging | 从源码仓库到 `pip install`，用 `pyproject.toml → build backend → wheel/sdist → installer` 串一次完整链路。 | [174](Python_碎片化复习手册_CPython_3.14.7.md#card-174)、[175](Python_碎片化复习手册_CPython_3.14.7.md#card-175) |
| 安全 | 分别判断“不可信字符串进入 eval”“进入 shell”“作为随机 token 来源”三种场景的第一安全边界。 | [181](Python_碎片化复习手册_CPython_3.14.7.md#card-181)、[182](Python_碎片化复习手册_CPython_3.14.7.md#card-182)、[185](Python_碎片化复习手册_CPython_3.14.7.md#card-185) |

---

## 30 秒随机复习

> 这一节只作为随机入口。答案默认折叠/跳回主卡，避免滚动时提前看到。

---

下面这些问题非常适合日常随机抽查。建议按“先答 → 标记置信度 → 再核对”的方式使用：

```text
2 = 能解释原因，并能预测一个变体
1 = 只记得结论，原因不稳
0 = 想不起来 / 判断错
```

这里的 `0/1/2` 只是**本次作答结果**，不是掌握度；长期状态仍只使用 `U/M0～M4`。

优先复习 0 分卡，其次 1 分卡；连续两次得到 2 分后再拉长复习间隔。能不用运行代码直接解释，并能处理一个小变体，才说明心智模型在变稳。

1. `a = b` 为什么通常不是复制对象？
2. `is` 与 `==` 的区别是什么？
3. 为什么 tuple 里仍然可以“看到内部 list 被修改”？
4. 为什么 mutable default argument 会跨调用共享？
5. 参数传递为什么不能简单说“list 引用传递、int 值传递”？
6. `global` 与 `nonlocal` 分别改变哪层绑定？
7. closure 为什么能在 outer 返回后继续访问状态？
8. late binding 到底绑定的是什么？
9. `@decorator` 的等价展开是什么？
10. 为什么 `functools.wraps` 值得用？
11. `bytes` 与 `str` 的边界是什么？
12. `append()` 与 `extend()` 差别是什么？
13. dict 为什么平均查找接近 O(1)？
14. dict 为什么保持插入顺序但仍然是哈希表？
15. `and/or` 为什么不一定返回 bool？
16. loop `else` 什么时候执行？
17. iterable 与 iterator 有什么区别？
18. generator 为什么通常只能消费一次？
19. `yield` 保存了什么执行状态？
20. `self` 为什么会被自动传入实例方法？
21. `super()` 为什么不是简单“父类对象”？
22. descriptor 与 property、method binding 有什么关系？
23. `finally` 为什么适合清理资源？
24. 为什么不应吞掉 `Exception`？
25. import 为什么会缓存 module object？
26. circular import 为什么经常是架构信号？
27. Type Hint 为什么不自动做 runtime validation？
28. `Any` 和 `object` 有什么区别？
29. Python 3.14 的注解求值发生了什么变化？
30. 文本文件为什么应明确 encoding？
31. `pickle.loads()` 为什么不能处理不可信数据？
32. thread、process、coroutine 分别在哪个层次？
33. GIL 为什么不等于线程安全？
34. Python 3.14 free-threaded 为什么不能直接理解成“所有 Python 都无 GIL”？
35. async function 调用为什么得到 coroutine object？
36. blocking call 为什么会卡 event loop？
37. TaskGroup 比裸 `create_task` 生命周期管理好在哪里？
38. 引用计数为什么解决不了循环引用？
39. 对象释放后 RSS 为什么不一定下降？
40. `dis` 能回答什么、不能保证什么？
41. subprocess 为什么优先 argument list？
42. 网络请求为什么一定要考虑 timeout？
43. retry 为什么必须考虑幂等性？
44. transaction 边界为什么是业务设计问题？
45. fixture 与 mock 分别解决什么问题？
46. patch 为什么要 patch 使用位置？
47. coverage 为什么不能等价测试质量？
48. profiler 与 benchmark 的问题分别是什么？
49. cache 为什么会延长对象生命周期？
50. virtual environment 为什么不是安全 sandbox？
51. wheel 和 sdist 有什么区别？
52. formatter、linter、type checker 为什么不是同一种工具？
53. `secrets` 为什么比 `random` 更适合 token？
54. EAFP 与 LBYL 什么时候各自更清楚？
55. composition 为什么经常比 inheritance 更容易演进？

---

---

### 自测答案与主知识卡索引

<details>
<summary>展开 55 道简短答案</summary>

> 建议先口头回答，再展开这里核对。答案刻意保持短，只用于校准心智模型。

1. 赋值通常只新增/改变名字到对象的绑定，不自动复制对象；复制需要显式 copy/deepcopy。 → [卡 6](Python_碎片化复习手册_CPython_3.14.7.md#card-006)
2. `==` 关注相等性语义，`is` 关注对象 identity；普通值比较通常用 `==`。 → [卡 8](Python_碎片化复习手册_CPython_3.14.7.md#card-008)
3. tuple 不允许替换自己保存的引用，但它引用的可变 list 仍可被原地修改。 → [卡 7](Python_碎片化复习手册_CPython_3.14.7.md#card-007)
4. 默认参数表达式在函数定义时求值；默认 list/dict 会被多次无参调用复用。 → [卡 16](Python_碎片化复习手册_CPython_3.14.7.md#card-016)
5. 参数模型对类型是一致的：形参名绑定到传入对象；差异来自对象可变性以及函数执行 mutate 还是 rebind。 → [卡 13](Python_碎片化复习手册_CPython_3.14.7.md#card-013)
6. `global` 指向模块全局绑定；`nonlocal` 指向最近的 enclosing function scope 绑定。 → [卡 23](Python_碎片化复习手册_CPython_3.14.7.md#card-023)
7. closure 保存了对 free variable 所在 cell/环境的引用，因此 outer frame 结束后相关对象仍可存活。 → [卡 24](Python_碎片化复习手册_CPython_3.14.7.md#card-024)
8. late binding 绑定的是变量/名称所在 cell，而不是在创建 lambda 时自动冻结当时的值。 → [卡 25](Python_碎片化复习手册_CPython_3.14.7.md#card-025)
9. `@decorator` 可理解为定义函数后执行 `func = decorator(func)`。 → [卡 26](Python_碎片化复习手册_CPython_3.14.7.md#card-026)
10. `wraps` 复制/维护 `__name__`、`__doc__`、`__wrapped__` 等元数据，利于调试、内省和工具链。 → [卡 26](Python_碎片化复习手册_CPython_3.14.7.md#card-026)
11. `str` 表示 Unicode 文本；`bytes` 表示原始字节。文本进出二进制边界需要显式 encode/decode。 → [卡 31](Python_碎片化复习手册_CPython_3.14.7.md#card-031)
12. `append(x)` 把 x 作为一个元素加入；`extend(iterable)` 逐个加入 iterable 的元素。 → [卡 34](Python_碎片化复习手册_CPython_3.14.7.md#card-034)
13. dict 基于哈希表，平均情况下 hash 定位让查找/插入接近 O(1)，最坏情况不能机械保证。 → [卡 38](Python_碎片化复习手册_CPython_3.14.7.md#card-038)
14. “保持插入顺序”描述迭代顺序；底层仍可使用哈希表维护 key 定位，两者不矛盾。 → [卡 38](Python_碎片化复习手册_CPython_3.14.7.md#card-038)
15. `and/or` 返回参与短路求值的操作数之一，而不是强制转换成 bool。 → [卡 44](Python_碎片化复习手册_CPython_3.14.7.md#card-044)
16. 循环正常耗尽且没有被 `break` 终止时执行 `else`。 → [卡 49](Python_碎片化复习手册_CPython_3.14.7.md#card-049)
17. iterable 能产生 iterator；iterator 还保存迭代状态并实现 `__next__()`。 → [卡 52](Python_碎片化复习手册_CPython_3.14.7.md#card-052)
18. generator object 本身就是 iterator，消费会推进内部状态；耗尽后不会自动重新开始。 → [卡 54](Python_碎片化复习手册_CPython_3.14.7.md#card-054)
19. `yield` 暂停 frame，保留指令位置、局部变量和执行上下文，以便下次恢复。 → [卡 54](Python_碎片化复习手册_CPython_3.14.7.md#card-054)
20. 函数对象是 descriptor；通过实例访问时 `__get__()` 产生 bound method，把实例预绑定为第一个参数。 → [卡 58](Python_碎片化复习手册_CPython_3.14.7.md#card-058)
21. `super()` 根据 MRO 从当前位置继续查找，不是“直接拿到父类对象”。 → [卡 61](Python_碎片化复习手册_CPython_3.14.7.md#card-061)
22. descriptor 控制属性访问；property 是 data descriptor，普通方法绑定也依赖函数 descriptor。 → [卡 66](Python_碎片化复习手册_CPython_3.14.7.md#card-066)
23. `finally` 无论正常返回还是异常传播通常都会运行，适合释放资源和恢复状态。 → [卡 73](Python_碎片化复习手册_CPython_3.14.7.md#card-073)
24. 过宽捕获后不处理会隐藏真实失败、破坏可观测性；只捕获能正确处理的异常。 → [卡 69](Python_碎片化复习手册_CPython_3.14.7.md#card-069)
25. 成功 import 后 module object 会进入 `sys.modules`，后续 import 通常复用它，避免重复执行模块顶层代码。 → [卡 79](Python_碎片化复习手册_CPython_3.14.7.md#card-079)
26. 它常暴露模块初始化顺序互相依赖和职责耦合；可以通过重新划分依赖方向或延迟导入解决。 → [卡 80](Python_碎片化复习手册_CPython_3.14.7.md#card-080)
27. Type Hint 主要给静态 checker/IDE/人阅读，Python 运行时默认不会按注解自动拒绝不匹配参数。 → [卡 82](Python_碎片化复习手册_CPython_3.14.7.md#card-082)
28. `object` 表示“某个具体但未知的普通对象”，使用前需缩窄；`Any` 会让静态检查器放弃很多检查。 → [卡 84](Python_碎片化复习手册_CPython_3.14.7.md#card-084)
29. 3.14 默认从 eager 变为 deferred evaluation；但 `from __future__ import annotations` 仍是字符串化语义。 → [卡 91](Python_碎片化复习手册_CPython_3.14.7.md#card-091)
30. 文本必须经编码映射到字节；不显式 encoding 会依赖环境默认值，导致跨机器/区域不稳定。 → [卡 92](Python_碎片化复习手册_CPython_3.14.7.md#card-092)
31. pickle 反序列化可以触发对象构造/代码路径，不是只读取“纯数据”；不可信输入可导致代码执行风险。 → [卡 96](Python_碎片化复习手册_CPython_3.14.7.md#card-096)
32. process 是 OS 资源/地址空间单位；thread 是进程内执行流；coroutine 是由语言/事件循环协作调度的可暂停计算。 → [卡 140](Python_碎片化复习手册_CPython_3.14.7.md#card-140)
33. GIL 只限制默认 CPython 同一时刻执行 Python 代码的线程数量，不保护你的多步骤业务不变量，也不消除 race condition。 → [卡 113](Python_碎片化复习手册_CPython_3.14.7.md#card-113)
34. free-threaded 是可选构建；GIL 还可能被显式启用或因不兼容 C extension 在运行时重新启用。 → [卡 114](Python_碎片化复习手册_CPython_3.14.7.md#card-114)
35. 调用 `async def` 不立即跑完整函数体，而创建 coroutine object；需要 await/Task 驱动它。 → [卡 116](Python_碎片化复习手册_CPython_3.14.7.md#card-116)
36. event loop 依赖任务主动让出执行权；同步阻塞调用不 await，会占住 loop 线程。 → [卡 119](Python_碎片化复习手册_CPython_3.14.7.md#card-119)
37. TaskGroup 把子任务生命周期绑定在结构化作用域内，并统一处理等待、失败和取消传播。 → [卡 117](Python_碎片化复习手册_CPython_3.14.7.md#card-117)
38. 循环中的对象彼此引用，即使外部不可达，单纯 refcount 也可能都不为 0，需要 cyclic GC 识别不可达对象图。 → [卡 125](Python_碎片化复习手册_CPython_3.14.7.md#card-125)
39. 对象释放只说明对象生命周期结束；allocator、分片、QSBR/mimalloc purge 或 OS 策略都可能让 RSS 暂时不降。 → [卡 128](Python_碎片化复习手册_CPython_3.14.7.md#card-128)
40. `dis` 能观察当前 CPython 字节码/指令形态；它不能保证跨版本/跨实现稳定，也不能单独证明高层语义性能。 → [卡 132](Python_碎片化复习手册_CPython_3.14.7.md#card-132)
41. argument list 避免 shell 再解析字符串；但还要防目标 CLI 自身的 option injection。 → [卡 144](Python_碎片化复习手册_CPython_3.14.7.md#card-144)
42. 网络可能无限慢、半连接或对端不响应；没有 timeout 就可能无限占用线程/任务/连接。 → [卡 148](Python_碎片化复习手册_CPython_3.14.7.md#card-148)
43. 重试会重复执行请求；非幂等操作可能重复扣款/创建资源，因此要设计 idempotency key 或幂等语义。 → [卡 148](Python_碎片化复习手册_CPython_3.14.7.md#card-148)
44. 事务决定哪些业务状态必须原子提交/回滚，边界过大或过小都会影响一致性、锁和并发。 → [卡 150](Python_碎片化复习手册_CPython_3.14.7.md#card-150)
45. fixture 提供可复用测试环境/依赖；mock/test double 替代边界依赖并观察交互，目标不同。 → [卡 156](Python_碎片化复习手册_CPython_3.14.7.md#card-156)
46. patch 应替换“被测代码实际查找该名字的位置”，而不是机械 patch 对象最初定义的位置。 → [卡 159](Python_碎片化复习手册_CPython_3.14.7.md#card-159)
47. coverage 只说明哪些代码被执行，不证明断言充分、边界完整或行为正确。 → [卡 161](Python_碎片化复习手册_CPython_3.14.7.md#card-161)
48. benchmark 回答“这一段/方案多快”；profiler 回答“时间/资源主要花在哪里”。 → [卡 167](Python_碎片化复习手册_CPython_3.14.7.md#card-167)
49. cache 本质保留对象引用以便复用，所以会延长被缓存 key/value 的生命周期并增加内存占用。 → [卡 169](Python_碎片化复习手册_CPython_3.14.7.md#card-169)
50. venv 隔离 Python package 依赖，不隔离 OS 权限、文件、网络、进程等安全边界。 → [卡 172](Python_碎片化复习手册_CPython_3.14.7.md#card-172)
51. wheel 是预构建 distribution 格式；sdist 是源代码分发，需要目标环境执行构建流程。 → [卡 175](Python_碎片化复习手册_CPython_3.14.7.md#card-175)
52. formatter 统一格式；linter 找风格/潜在错误；type checker 做静态类型分析，三者问题域不同。 → [卡 179](Python_碎片化复习手册_CPython_3.14.7.md#card-179)
53. `random` 面向模拟/一般随机；`secrets` 使用适合安全 token/凭证场景的随机源和 API。 → [卡 185](Python_碎片化复习手册_CPython_3.14.7.md#card-185)
54. EAFP 在异常确实代表少见失败且竞争窗口存在时清楚；LBYL 在检查本身便宜、语义明确且不是竞态检查时可读。 → [卡 189](Python_碎片化复习手册_CPython_3.14.7.md#card-189)
55. composition 通过显式对象协作组合行为，依赖面更窄；inheritance 把子类绑到父类行为/MRO，演进时耦合通常更强。 → [卡 192](Python_碎片化复习手册_CPython_3.14.7.md#card-192)

---

</details>

## 核心知识训练中心

这一篇不新增知识点，而是把 55 张高频核心卡转换成四种能力测试：

```text
基础题      → 能否复述/预测核心规则
变体题      → 条件变化后还能否守住边界
真实开发 Bug → 能否从症状定位到机制并给出修复方向
面试追问    → 能否用准确术语解释原理、权衡与边界
```

建议不要一次刷完。每次选 1～3 张：先做变体预测；需要训练排错时做真实 Bug；准备面试或冲击 M4 时再做面试追问。每个训练块都可跳回对应主知识卡。

### 30.1 训练索引

| 训练 | 主题 | 回到正文 |
|---:|---|---|
| [004](#train-004) | Python 对象：identity、type、value | [原卡](Python_碎片化复习手册_CPython_3.14.7.md#card-004) |
| [006](#train-006) | 引用与对象共享 | [原卡](Python_碎片化复习手册_CPython_3.14.7.md#card-006) |
| [007](#train-007) | Mutable 与 Immutable | [原卡](Python_碎片化复习手册_CPython_3.14.7.md#card-007) |
| [008](#train-008) | `is` 与 `==` | [原卡](Python_碎片化复习手册_CPython_3.14.7.md#card-008) |
| [009](#train-009) | 浅拷贝与深拷贝 | [原卡](Python_碎片化复习手册_CPython_3.14.7.md#card-009) |
| [013](#train-013) | Python 参数传递模型 | [原卡](Python_碎片化复习手册_CPython_3.14.7.md#card-013) |
| [014](#train-014) | 完整参数语法 | [原卡](Python_碎片化复习手册_CPython_3.14.7.md#card-014) |
| [016](#train-016) | 默认参数在定义时求值 | [原卡](Python_碎片化复习手册_CPython_3.14.7.md#card-016) |
| [022](#train-022) | LEGB 名称查找 | [原卡](Python_碎片化复习手册_CPython_3.14.7.md#card-022) |
| [023](#train-023) | `global` 与 `nonlocal` | [原卡](Python_碎片化复习手册_CPython_3.14.7.md#card-023) |
| [024](#train-024) | 闭包 Closure | [原卡](Python_碎片化复习手册_CPython_3.14.7.md#card-024) |
| [025](#train-025) | 闭包的 Late Binding | [原卡](Python_碎片化复习手册_CPython_3.14.7.md#card-025) |
| [026](#train-026) | 装饰器的本质 | [原卡](Python_碎片化复习手册_CPython_3.14.7.md#card-026) |
| [031](#train-031) | `str`、Unicode、编码与解码 | [原卡](Python_碎片化复习手册_CPython_3.14.7.md#card-031) |
| [034](#train-034) | List：动态数组心智模型 | [原卡](Python_碎片化复习手册_CPython_3.14.7.md#card-034) |
| [038](#train-038) | Dict：哈希表与顺序 | [原卡](Python_碎片化复习手册_CPython_3.14.7.md#card-038) |
| [044](#train-044) | Truthiness 与短路求值 | [原卡](Python_碎片化复习手册_CPython_3.14.7.md#card-044) |
| [047](#train-047) | `for` 的真实基础：迭代协议 | [原卡](Python_碎片化复习手册_CPython_3.14.7.md#card-047) |
| [052](#train-052) | Iterable 与 Iterator | [原卡](Python_碎片化复习手册_CPython_3.14.7.md#card-052) |
| [054](#train-054) | Generator 与 `yield` | [原卡](Python_碎片化复习手册_CPython_3.14.7.md#card-054) |
| [057](#train-057) | Class、Instance 与 Attribute | [原卡](Python_碎片化复习手册_CPython_3.14.7.md#card-057) |
| [058](#train-058) | `self`、实例方法、classmethod、staticmethod | [原卡](Python_碎片化复习手册_CPython_3.14.7.md#card-058) |
| [061](#train-061) | MRO 与 `super()` | [原卡](Python_碎片化复习手册_CPython_3.14.7.md#card-061) |
| [065](#train-065) | Attribute Lookup、`__getattr__` 与 `__getattribute__` | [原卡](Python_碎片化复习手册_CPython_3.14.7.md#card-065) |
| [066](#train-066) | Descriptor | [原卡](Python_碎片化复习手册_CPython_3.14.7.md#card-066) |
| [073](#train-073) | Context Manager 与 `with` | [原卡](Python_碎片化复习手册_CPython_3.14.7.md#card-073) |
| [079](#train-079) | `sys.modules` 与 Import Cache | [原卡](Python_碎片化复习手册_CPython_3.14.7.md#card-079) |
| [080](#train-080) | Circular Import | [原卡](Python_碎片化复习手册_CPython_3.14.7.md#card-080) |
| [082](#train-082) | Type Hint 不改变动态类型本质 | [原卡](Python_碎片化复习手册_CPython_3.14.7.md#card-082) |
| [106](#train-106) | Concurrency 与 Parallelism | [原卡](Python_碎片化复习手册_CPython_3.14.7.md#card-106) |
| [110](#train-110) | Race Condition 与 Lock | [原卡](Python_碎片化复习手册_CPython_3.14.7.md#card-110) |
| [113](#train-113) | 传统 GIL 心智模型 | [原卡](Python_碎片化复习手册_CPython_3.14.7.md#card-113) |
| [114](#train-114) | Free-threaded CPython 3.14 | [原卡](Python_碎片化复习手册_CPython_3.14.7.md#card-114) |
| [115](#train-115) | Asyncio 核心模型 | [原卡](Python_碎片化复习手册_CPython_3.14.7.md#card-115) |
| [117](#train-117) | Task、TaskGroup 与结构化并发 | [原卡](Python_碎片化复习手册_CPython_3.14.7.md#card-117) |
| [119](#train-119) | Blocking Code 会卡 Event Loop | [原卡](Python_碎片化复习手册_CPython_3.14.7.md#card-119) |
| [124](#train-124) | Reference Counting | [原卡](Python_碎片化复习手册_CPython_3.14.7.md#card-124) |
| [125](#train-125) | 循环引用与 Cyclic GC | [原卡](Python_碎片化复习手册_CPython_3.14.7.md#card-125) |
| [144](#train-144) | `subprocess` | [原卡](Python_碎片化复习手册_CPython_3.14.7.md#card-144) |
| [148](#train-148) | Timeout、Retry 与 Idempotency | [原卡](Python_碎片化复习手册_CPython_3.14.7.md#card-148) |
| [150](#train-150) | Transaction | [原卡](Python_碎片化复习手册_CPython_3.14.7.md#card-150) |
| [156](#train-156) | Fixture | [原卡](Python_碎片化复习手册_CPython_3.14.7.md#card-156) |
| [158](#train-158) | Dummy、Stub、Spy、Mock、Fake | [原卡](Python_碎片化复习手册_CPython_3.14.7.md#card-158) |
| [159](#train-159) | Patch 的位置 | [原卡](Python_碎片化复习手册_CPython_3.14.7.md#card-159) |
| [166](#train-166) | Big O 与 Python 容器复杂度 | [原卡](Python_碎片化复习手册_CPython_3.14.7.md#card-166) |
| [169](#train-169) | Cache | [原卡](Python_碎片化复习手册_CPython_3.14.7.md#card-169) |
| [170](#train-170) | N+1、批处理与 IO 性能 | [原卡](Python_碎片化复习手册_CPython_3.14.7.md#card-170) |
| [174](#train-174) | `pyproject.toml` | [原卡](Python_碎片化复习手册_CPython_3.14.7.md#card-174) |
| [175](#train-175) | Wheel 与 Source Distribution | [原卡](Python_碎片化复习手册_CPython_3.14.7.md#card-175) |
| [181](#train-181) | `eval()` 与 `exec()` | [原卡](Python_碎片化复习手册_CPython_3.14.7.md#card-181) |
| [182](#train-182) | Command Injection | [原卡](Python_碎片化复习手册_CPython_3.14.7.md#card-182) |
| [185](#train-185) | `random` vs `secrets` | [原卡](Python_碎片化复习手册_CPython_3.14.7.md#card-185) |
| [189](#train-189) | EAFP 与 LBYL | [原卡](Python_碎片化复习手册_CPython_3.14.7.md#card-189) |
| [190](#train-190) | Pure Function 与 Side Effect | [原卡](Python_碎片化复习手册_CPython_3.14.7.md#card-190) |
| [192](#train-192) | Composition vs Inheritance | [原卡](Python_碎片化复习手册_CPython_3.14.7.md#card-192) |

### 30.2 四档训练

<a id="train-004"></a>

### 训练 004 · Python 对象：identity、type、value

[↩ 返回知识卡 4](Python_碎片化复习手册_CPython_3.14.7.md#card-004)

<details>
<summary><strong>基础题</strong></summary>

```python
a = [1, 2]
b = [1, 2]
c = a
```

不运行代码，判断 `a == b`、`a is b`、`a is c`。

**揭晓：** `True / False / True`。`==` 讨论值；`is` 讨论对象身份。

</details>

<details>
<summary><strong>变体题</strong></summary>

为什么不能根据 `id(a)` 的某个具体数字去推断对象的物理内存地址？

**揭晓：** `id()` 保证的是对象生命周期内的 identity 值；CPython 常把它实现得像地址，但这是实现细节，不是 Python 语言承诺。

</details>

<details>
<summary><strong>真实开发 Bug</strong></summary>

缓存层用 `payload is cached_payload` 判断“数据有没有变化”，结果相同内容但重新解析出的 dict 总被判为变化。修复应改成什么？

**诊断：** 业务想比较 value，应使用合适的 `==` 或业务字段/哈希，而不是 identity。

</details>

<details>
<summary><strong>面试追问</strong></summary>

“Python 一切皆对象”对函数、类、模块意味着什么？

**回答要点：** 它们都可以被绑定名称、传参、返回、存入容器；这直接支撑高阶函数、装饰器、反射与元编程。

</details>


---

<a id="train-006"></a>

### 训练 006 · 引用与对象共享

[↩ 返回知识卡 6](Python_碎片化复习手册_CPython_3.14.7.md#card-006)

<details>
<summary><strong>基础题</strong></summary>

```python
a = {"items": []}
b = a
b["items"].append(1)
```

`a` 是什么？

**揭晓：** `{"items": [1]}`，因为 `a`、`b` 引用同一个 dict，内部又引用同一个 list。

</details>

<details>
<summary><strong>变体题</strong></summary>

```python
a = []
b = [a, a]
b[0].append(1)
```

`b` 的两个元素是什么关系？

**揭晓：** 两个槽位都引用同一个 list，所以 `b == [[1], [1]]`。

</details>

<details>
<summary><strong>真实开发 Bug</strong></summary>

配置模板 dict 被多个请求复用，请求 A 在嵌套 `headers` 中添加字段后，请求 B 也看到了。根因是什么？

**诊断：** 共享了可变对象图；需要明确每请求对象边界，必要时重建或复制对应层级。

</details>

<details>
<summary><strong>面试追问</strong></summary>

“容器装的是对象还是对象引用？”如何用这个模型解释嵌套对象共享？

**回答要点：** Python 容器保存的是对对象的引用；外层对象独立不代表内部对象也独立。

</details>


---

<a id="train-007"></a>

### 训练 007 · Mutable 与 Immutable

[↩ 返回知识卡 7](Python_碎片化复习手册_CPython_3.14.7.md#card-007)

<details>
<summary><strong>基础题</strong></summary>

```python
a = [1]
b = a
a += [2]
```

`a is b` 是什么？

**揭晓：** 通常为 `True`；list 的 `+=` 是原地扩展语义。

</details>

<details>
<summary><strong>变体题</strong></summary>

```python
a = ([1],)
a[0].append(2)
```

为什么 tuple “不可变”却能看到变化？

**揭晓：** tuple 自己保存的引用没变；变化发生在它引用的可变 list 内部。

</details>

<details>
<summary><strong>真实开发 Bug</strong></summary>

开发者认为 `tuple` 配置绝对安全，把可变 dict 放进 tuple 后仍被其他代码修改。问题在哪？

**诊断：** immutable 只约束对象自身结构，不递归保证其引用对象不可变。

</details>

<details>
<summary><strong>面试追问</strong></summary>

可变性为什么会影响 hashability、默认参数和并发共享状态？

**回答要点：** 原地状态变化会破坏稳定哈希、跨调用共享默认对象、以及并发读改写的一致性。

</details>


---

<a id="train-008"></a>

### 训练 008 · `is` 与 `==`

[↩ 返回知识卡 8](Python_碎片化复习手册_CPython_3.14.7.md#card-008)

<details>
<summary><strong>基础题</strong></summary>

什么时候应优先写 `x is None`？

**揭晓：** 判断是否就是 `None` 单例时；这表达 identity 语义，也不受自定义 `__eq__` 影响。

</details>

<details>
<summary><strong>变体题</strong></summary>

```python
a = 256
b = 256
print(a is b)
```

为什么即使某次运行得到 `True`，代码也不能依赖它？

**揭晓：** 小整数/常量复用属于实现与编译上下文优化；值比较应使用 `==`。

</details>

<details>
<summary><strong>真实开发 Bug</strong></summary>

生产代码写 `status is "ready"`，测试偶尔通过、换构建环境后失败。如何 review？

**诊断：** 字符串值比较必须用 `==`；identity 不能承担业务值语义。

</details>

<details>
<summary><strong>面试追问</strong></summary>

自定义 `__eq__` 后，`is` 会受影响吗？

**回答要点：** 不会；`is` 是对象身份操作，不能被重载。

</details>


---

<a id="train-009"></a>

### 训练 009 · 浅拷贝与深拷贝

[↩ 返回知识卡 9](Python_碎片化复习手册_CPython_3.14.7.md#card-009)

<details>
<summary><strong>基础题</strong></summary>

```python
import copy
a = [[1]]
b = copy.copy(a)
b[0].append(2)
```

`a` 是什么？

**揭晓：** `[[1, 2]]`；浅拷贝只复制外层 list。

</details>

<details>
<summary><strong>变体题</strong></summary>

为什么 `deepcopy()` 不是“任何时候都更安全”？

**揭晓：** 它可能复制不该复制的共享语义、代价高，并且外部资源/锁/连接等对象并不适合被无脑深拷贝。

</details>

<details>
<summary><strong>真实开发 Bug</strong></summary>

Web 请求先 `cfg = DEFAULT.copy()`，随后修改 `cfg["db"]["timeout"]`，全局默认值也变了。

**诊断：** `dict.copy()` 是浅拷贝，嵌套 `db` dict 仍共享；应只重建需要独立的子结构或使用明确的数据模型。

</details>

<details>
<summary><strong>面试追问</strong></summary>

`deepcopy()` 怎么避免面对循环引用时无限递归？

**回答要点：** 实现会维护已经复制过的对象映射（memo），复用已创建的副本。

</details>


---

<a id="train-013"></a>

### 训练 013 · Python 参数传递模型

[↩ 返回知识卡 13](Python_碎片化复习手册_CPython_3.14.7.md#card-013)

<details>
<summary><strong>基础题</strong></summary>

```python
def f(items):
    items.append(1)
    items = []

a = []
f(a)
```

最终 `a` 是什么？

**揭晓：** `[1]`。`append` 修改共享对象；之后 `items = []` 只是重新绑定局部名字。

</details>

<details>
<summary><strong>变体题</strong></summary>

为什么“list 是引用传递、int 是值传递”是一个危险简化？

**揭晓：** 参数绑定规则统一；差异来自对象可变性以及函数执行的是 mutate 还是 rebind。

</details>

<details>
<summary><strong>真实开发 Bug</strong></summary>

工具函数 `normalize(data)` 为了“方便”直接修改传入 dict，调用方复用原始数据时出现状态污染。

**诊断：** API 没有明确 mutation contract；要么返回新对象，要么在命名/文档中清楚声明会原地修改。

</details>

<details>
<summary><strong>面试追问</strong></summary>

用“名称绑定”而非“值/引用二选一”解释一次函数调用。

**回答要点：** 实参表达式先得到对象，形参是在新局部作用域建立的名称绑定；多个名称可能共享对象。

</details>


---

<a id="train-014"></a>

### 训练 014 · 完整参数语法

[↩ 返回知识卡 14](Python_碎片化复习手册_CPython_3.14.7.md#card-014)

<details>
<summary><strong>基础题</strong></summary>

```python
def f(a, /, b=1, *args, c, **kwargs):
    return a, b, args, c, kwargs
```

`f(10, 20, 30, c=40, x=50)` 返回什么？

**揭晓：** `(10, 20, (30,), 40, {"x": 50})`。

</details>

<details>
<summary><strong>变体题</strong></summary>

为什么 API 设计中 `timeout` 常适合做 keyword-only？

**揭晓：** 调用 `request(url, timeout=3)` 比位置值 `request(url, 3)` 更清楚，且将来扩展签名更稳。

</details>

<details>
<summary><strong>真实开发 Bug</strong></summary>

公共库大量暴露 `def api(*args, **kwargs)`，IDE 无法补全，参数拼错只能运行时发现。

**诊断：** 过度吞掉签名；仅在包装/转发等确有需要时使用，并尽量保留可检查的签名与类型信息。

</details>

<details>
<summary><strong>面试追问</strong></summary>

`/` 与 `*` 对 API 兼容性分别有什么价值？

**回答要点：** `/` 让参数名不成为调用契约；`*` 强制关键字调用，提高语义清晰度并降低位置参数扩展风险。

</details>


---

<a id="train-016"></a>

### 训练 016 · 默认参数在定义时求值

[↩ 返回知识卡 16](Python_碎片化复习手册_CPython_3.14.7.md#card-016)

<details>
<summary><strong>基础题</strong></summary>

```python
def f(x=[]):
    x.append(1)
    return x

print(f())
print(f())
```

**揭晓：** `[1]`、`[1, 1]`。

</details>

<details>
<summary><strong>变体题</strong></summary>

```python
from datetime import datetime

def stamp(now=datetime.now()):
    return now
```

为什么它也有同类问题，即使默认值不是 list？

**揭晓：** `datetime.now()` 在函数定义时只执行一次；这里的问题是“求值时机”，不只“可变性”。

</details>

<details>
<summary><strong>真实开发 Bug</strong></summary>

定时任务函数默认 `started_at=datetime.now()`，日志里一天内大量任务拥有完全相同开始时间。

**诊断：** 改用 `None`/sentinel，在调用时生成动态值。

</details>

<details>
<summary><strong>面试追问</strong></summary>

默认参数为什么有时可以故意用可变对象？为什么仍通常不推荐？

**回答要点：** 可利用定义时单次求值保存状态/缓存；但语义隐蔽、难测试、易造成跨调用耦合。

</details>


---

<a id="train-022"></a>

### 训练 022 · LEGB 名称查找

[↩ 返回知识卡 22](Python_碎片化复习手册_CPython_3.14.7.md#card-022)

<details>
<summary><strong>基础题</strong></summary>

函数内部读取 `name` 时，LEGB 的查找顺序是什么？

**揭晓：** Local → Enclosing → Global → Builtins。

</details>

<details>
<summary><strong>变体题</strong></summary>

```python
x = 10
def f():
    print(x)
    x = 20
```

为什么不是先打印全局 `10`？

**揭晓：** 编译函数体时 `x = 20` 使 `x` 被判定为局部名称；读取发生在局部赋值前，会触发 `UnboundLocalError`。

</details>

<details>
<summary><strong>真实开发 Bug</strong></summary>

模块写了 `list = []`，下方代码调用 `list(iterable)` 报错。

**诊断：** Global 名称遮蔽 Builtins；避免 shadow builtin。

</details>

<details>
<summary><strong>面试追问</strong></summary>

LEGB 是“运行时逐层扫描所有字典”这么简单吗？

**回答要点：** 这是语言层心智模型；CPython 会在编译阶段分类 local/free/global 名称，并生成不同字节码访问路径。

</details>


---

<a id="train-023"></a>

### 训练 023 · `global` 与 `nonlocal`

[↩ 返回知识卡 23](Python_碎片化复习手册_CPython_3.14.7.md#card-023)

<details>
<summary><strong>基础题</strong></summary>

`global x` 和 `nonlocal x` 分别改变哪个作用域中的绑定？

**揭晓：** `global` 指向模块全局绑定；`nonlocal` 指向最近的 enclosing function scope 绑定。

</details>

<details>
<summary><strong>变体题</strong></summary>

为什么 `nonlocal` 不能创建一个此前不存在的 enclosing 名称？

**揭晓：** 它要求在外层函数作用域中已经存在可绑定名称，否则无法解析目标 cell。

</details>

<details>
<summary><strong>真实开发 Bug</strong></summary>

一个闭包计数器忘写 `nonlocal count`，执行 `count += 1` 时触发 `UnboundLocalError`。

**诊断：** 赋值让 `count` 被认作当前局部；应显式 `nonlocal count` 或改成更清晰的状态对象。

</details>

<details>
<summary><strong>面试追问</strong></summary>

什么时候应该避免 `global/nonlocal`，即使语法允许？

**回答要点：** 当共享可变状态让依赖隐蔽、测试困难或并发风险升高时，优先显式传递状态或封装对象。

</details>


---

<a id="train-024"></a>

### 训练 024 · 闭包 Closure

[↩ 返回知识卡 24](Python_碎片化复习手册_CPython_3.14.7.md#card-024)

<details>
<summary><strong>基础题</strong></summary>

```python
def make_add(n):
    def add(x):
        return x + n
    return add
```

`make_add(10)` 返回后，为什么 `n` 仍可被访问？

**揭晓：** 返回函数形成 closure，保留了对 free variable `n` 所在 cell 的引用。

</details>

<details>
<summary><strong>变体题</strong></summary>

闭包“保存变量”是否等于把当时的值复制一份？

**揭晓：** 不一定。闭包保存的是与自由变量绑定相关的 cell；如果绑定后来改变，闭包可能观察到变化。

</details>

<details>
<summary><strong>真实开发 Bug</strong></summary>

工厂函数返回几十个 handler，每个闭包都捕获大型配置对象，导致配置长期不能释放。

**诊断：** closure 延长了被捕获对象生命周期；只捕获真正需要的小状态，或改为显式对象/参数。

</details>

<details>
<summary><strong>面试追问</strong></summary>

闭包和 callable class 怎么选？

**回答要点：** 少量封闭状态和简单行为适合闭包；需要多个方法、显式状态、可检查生命周期时 class 更清楚。

</details>


---

<a id="train-025"></a>

### 训练 025 · 闭包的 Late Binding

[↩ 返回知识卡 25](Python_碎片化复习手册_CPython_3.14.7.md#card-025)

<details>
<summary><strong>基础题</strong></summary>

```python
funcs = [lambda: i for i in range(3)]
print([f() for f in funcs])
```

**揭晓：** 通常 `[2, 2, 2]`；调用时才从 closure cell 读取最终 `i`。

</details>

<details>
<summary><strong>变体题</strong></summary>

为什么 `lambda i=i: i` 能修复？

**揭晓：** 默认参数表达式在函数创建时求值，把当次 `i` 绑定进每个函数自己的默认参数。

</details>

<details>
<summary><strong>真实开发 Bug</strong></summary>

循环注册三个按钮回调，点击任一按钮都处理最后一个 ID。

**诊断：** late binding；可以用默认参数冻结值、`functools.partial`，或创建新的工厂作用域。

</details>

<details>
<summary><strong>面试追问</strong></summary>

Late binding 是 lambda 特有问题吗？

**回答要点：** 不是；普通嵌套 `def` 捕获同一个循环变量也一样。

</details>


---

<a id="train-026"></a>

### 训练 026 · 装饰器的本质

[↩ 返回知识卡 26](Python_碎片化复习手册_CPython_3.14.7.md#card-026)

<details>
<summary><strong>基础题</strong></summary>

```python
@trace
def f():
    pass
```

不用 `@` 写出等价绑定。

**揭晓：** 先创建原函数，再执行 `f = trace(f)`。

</details>

<details>
<summary><strong>变体题</strong></summary>

为什么装饰器通常在模块 import/函数定义阶段执行，而 wrapper 在函数调用时执行？

**揭晓：** 装饰表达式参与函数对象创建后的重新绑定；返回的 wrapper 才是以后真正被调用的对象。

</details>

<details>
<summary><strong>真实开发 Bug</strong></summary>

自定义装饰器没用 `functools.wraps`，框架拿到的函数名、docstring、annotation 都变成 wrapper 的。

**诊断：** 元数据丢失；用 `@wraps(func)`，必要时还要考虑签名保持。

</details>

<details>
<summary><strong>面试追问</strong></summary>

装饰器和显式函数组合相比，最大的设计风险是什么？

**回答要点：** 行为被隐式包裹，调用链/异常/性能/状态可能不透明；应控制层数并保持单一职责。

</details>


---

<a id="train-031"></a>

### 训练 031 · `str`、Unicode、编码与解码

[↩ 返回知识卡 31](Python_碎片化复习手册_CPython_3.14.7.md#card-031)

<details>
<summary><strong>基础题</strong></summary>

`str.encode()` 和 `bytes.decode()` 的方向分别是什么？

**揭晓：** `str → bytes` 是 encode；`bytes → str` 是 decode。

</details>

<details>
<summary><strong>变体题</strong></summary>

同一个字符的 `len(text)` 与 `len(text.encode("utf-8"))` 为什么可能不同？

**揭晓：** 前者按 Unicode code point 数量计，后者按编码后的字节数计。

</details>

<details>
<summary><strong>真实开发 Bug</strong></summary>

服务从网络收到 UTF-8 bytes，却直接和 Python `str` 比较，导致条件永远不匹配或类型错误。

**诊断：** 明确 text/binary boundary，在 IO 边界只解码一次，内部尽量统一使用 `str`。

</details>

<details>
<summary><strong>面试追问</strong></summary>

“Unicode”和“UTF-8”是什么关系？

**回答要点：** Unicode 定义字符/码点体系；UTF-8 是将 Unicode code point 编码成 bytes 的一种编码方案。

</details>


---

<a id="train-034"></a>

### 训练 034 · List：动态数组心智模型

[↩ 返回知识卡 34](Python_碎片化复习手册_CPython_3.14.7.md#card-034)

<details>
<summary><strong>基础题</strong></summary>

为什么 `list.append()` 平均是 O(1)，但不是每次严格 O(1)？

**揭晓：** list 预留容量；多数 append 只写入槽位，偶尔需要扩容并复制引用，属于摊销 O(1)。

</details>

<details>
<summary><strong>变体题</strong></summary>

为什么在 list 头部不断 `insert(0, x)` 通常是坏选择？

**揭晓：** 现有元素引用需要整体移动，单次接近 O(n)；队列头尾操作通常考虑 `deque`。

</details>

<details>
<summary><strong>真实开发 Bug</strong></summary>

消费者用 `items.pop(0)` 处理几十万任务，CPU 时间随队列长度增长。

**诊断：** 数据结构与访问模式不匹配；改用 `collections.deque.popleft()`。

</details>

<details>
<summary><strong>面试追问</strong></summary>

list 中保存的是对象本体还是引用？这如何影响复制和内存估算？

**回答要点：** 主要是对象引用数组；`sys.getsizeof(list)` 不递归包含被引用对象总大小。

</details>


---

<a id="train-038"></a>

### 训练 038 · Dict：哈希表与顺序

[↩ 返回知识卡 38](Python_碎片化复习手册_CPython_3.14.7.md#card-038)

<details>
<summary><strong>基础题</strong></summary>

为什么 dict 查找平均接近 O(1)？

**揭晓：** 对 key 计算 hash 后定位哈希表槽位，通常无需线性扫描全部键。

</details>

<details>
<summary><strong>变体题</strong></summary>

两个对象 `a == b` 且都可哈希时，`hash(a)` 与 `hash(b)` 应满足什么关系？

**揭晓：** 必须相等；否则会破坏哈希容器的等价性约束。

</details>

<details>
<summary><strong>真实开发 Bug</strong></summary>

自定义 key 对象实现了基于可变字段的 `__hash__`，插入 dict 后字段改变，随后“明明在 dict 里却查不到”。

**诊断：** hash/equality 参与字段必须在作为 key 期间保持稳定。

</details>

<details>
<summary><strong>面试追问</strong></summary>

Python dict 保持插入顺序后，是否意味着它变成“排序字典”？

**回答要点：** 不是；它记录插入顺序，不会按 key 大小自动排序。

</details>


---

<a id="train-044"></a>

### 训练 044 · Truthiness 与短路求值

[↩ 返回知识卡 44](Python_碎片化复习手册_CPython_3.14.7.md#card-044)

<details>
<summary><strong>基础题</strong></summary>

```python
value = "" or "default"
```

`value` 是什么？

**揭晓：** `"default"`。`or` 返回操作数本身，不强制转成 bool。

</details>

<details>
<summary><strong>变体题</strong></summary>

为什么 `timeout = user_timeout or 30` 可能错误处理合法值 `0`？

**揭晓：** `0` 是 falsy，会被误当成“没有提供”。若业务区分 `0` 与 `None`，应显式判断。

</details>

<details>
<summary><strong>真实开发 Bug</strong></summary>

分页接口写 `limit = request.limit or 100`，用户明确传 `0` 想禁用返回却得到 100。

**诊断：** 把 falsy 当 missing；使用 sentinel/`is None` 按业务语义判断。

</details>

<details>
<summary><strong>面试追问</strong></summary>

对象 truthiness 的判定顺序是什么？

**回答要点：** 类型可通过 `__bool__` 定义；若没有，可参考 `__len__`；否则一般视为真。

</details>


---

<a id="train-047"></a>

### 训练 047 · `for` 的真实基础：迭代协议

[↩ 返回知识卡 47](Python_碎片化复习手册_CPython_3.14.7.md#card-047)

<details>
<summary><strong>基础题</strong></summary>

把 `for x in obj:` 用 `iter()`、`next()`、`StopIteration` 改写成等价心智模型。

**揭晓：** 先 `it = iter(obj)`，循环调用 `next(it)`，捕获 `StopIteration` 结束。

</details>

<details>
<summary><strong>变体题</strong></summary>

为什么实现了 `__getitem__(0...)` 的老式对象有时也能被迭代？

**揭晓：** Python 还存在序列迭代回退协议；但现代自定义 iterable 应明确实现 `__iter__`。

</details>

<details>
<summary><strong>真实开发 Bug</strong></summary>

自定义容器的 `__iter__` 返回 `self`，但对象本身没有正确维护独立迭代状态，嵌套两层 for 相互干扰。

**诊断：** 可迭代容器通常每次 `__iter__` 返回新的 iterator；只有 iterator 自身通常返回 `self`。

</details>

<details>
<summary><strong>面试追问</strong></summary>

Iterable 与 Iterator 的最小协议分别是什么？

**回答要点：** iterable 能产生 iterator；iterator 实现 `__next__` 并且 `iter(iterator) is iterator`。

</details>


---

<a id="train-052"></a>

### 训练 052 · Iterable 与 Iterator

[↩ 返回知识卡 52](Python_碎片化复习手册_CPython_3.14.7.md#card-052)

<details>
<summary><strong>基础题</strong></summary>

为什么 list 可以重复遍历，而一个 iterator 常被耗尽？

**揭晓：** list 是 iterable，每次可产生新的 iterator；iterator 自己保存当前遍历状态。

</details>

<details>
<summary><strong>变体题</strong></summary>

```python
it = iter([1, 2])
a = iter(it)
```

通常 `a is it` 为什么为真？

**揭晓：** iterator protocol 要求 iterator 的 `__iter__` 返回自身。

</details>

<details>
<summary><strong>真实开发 Bug</strong></summary>

函数参数接收 `records`，先 `sum(1 for _ in records)` 统计数量，再 for 处理；传 generator 时第二遍为空。

**诊断：** 不应默认 iterable 可重复消费；需要单遍处理、物化成 list，或要求可重入 iterable。

</details>

<details>
<summary><strong>面试追问</strong></summary>

API 类型标注用 `Iterable[T]` 与 `Iterator[T]` 分别暗示什么？

**回答要点：** `Iterable` 只承诺可获取 iterator；`Iterator` 暗示调用方拿到的是带消费状态的单个迭代器。

</details>


---

<a id="train-054"></a>

### 训练 054 · Generator 与 `yield`

[↩ 返回知识卡 54](Python_碎片化复习手册_CPython_3.14.7.md#card-054)

<details>
<summary><strong>基础题</strong></summary>

调用一个包含 `yield` 的 generator function 时，函数体会立刻跑到底吗？

**揭晓：** 不会；调用先返回 generator object，实际执行在迭代/`next()` 时推进，并在 `yield` 处挂起。

</details>

<details>
<summary><strong>变体题</strong></summary>

为什么 generator 能保存局部变量状态，却不等于“后台线程”？

**揭晓：** 它保存暂停的 frame/执行状态，但只有调用方推进它时才执行，没有自动并行调度。

</details>

<details>
<summary><strong>真实开发 Bug</strong></summary>

代码返回数据库查询 generator 后立即关闭连接；调用方稍后迭代才真正访问 cursor，于是报连接已关闭。

**诊断：** lazy evaluation 改变了资源生命周期；要在资源有效期内消费，或让 generator 自己管理上下文。

</details>

<details>
<summary><strong>面试追问</strong></summary>

Generator expression 相比 list comprehension 的核心 trade-off？

**回答要点：** 更低峰值内存和惰性消费，但单次、延迟执行、异常与资源生命周期也被推迟。

</details>


---

<a id="train-057"></a>

### 训练 057 · Class、Instance 与 Attribute

[↩ 返回知识卡 57](Python_碎片化复习手册_CPython_3.14.7.md#card-057)

<details>
<summary><strong>基础题</strong></summary>

```python
class A:
    tags = []

a = A(); b = A()
a.tags.append("x")
```

`b.tags` 是什么？

**揭晓：** `['x']`，两个实例都从类属性读取同一个 list。

</details>

<details>
<summary><strong>变体题</strong></summary>

执行 `a.tags = []` 后，为什么 `b.tags` 仍指向原类属性？

**揭晓：** 给实例赋值会在 `a.__dict__` 创建同名实例属性，遮蔽类属性，而不是修改类属性。

</details>

<details>
<summary><strong>真实开发 Bug</strong></summary>

模型类把 `errors = []` 写成类属性，每个请求实例的错误相互串台。

**诊断：** 可变实例状态应在 `__init__` / dataclass `default_factory` 中创建。

</details>

<details>
<summary><strong>面试追问</strong></summary>

实例属性查找为什么不只是查 `obj.__dict__`？

**回答要点：** 还涉及 class、MRO、descriptor，以及 `__getattribute__`/`__getattr__` 协议。

</details>


---

<a id="train-058"></a>

### 训练 058 · `self`、实例方法、classmethod、staticmethod

[↩ 返回知识卡 58](Python_碎片化复习手册_CPython_3.14.7.md#card-058)

<details>
<summary><strong>基础题</strong></summary>

`obj.method(1)` 为什么可粗略理解成 `Cls.method(obj, 1)`？

**揭晓：** 函数作为 class attribute 是 non-data descriptor，访问时会创建 bound method，把实例绑定为第一个参数。

</details>

<details>
<summary><strong>变体题</strong></summary>

`classmethod` 第一个参数为什么是实际调用类而不是“定义它的那个类”固定值？

**揭晓：** descriptor 绑定的是调用上下文中的 class，因此继承时可获得子类。

</details>

<details>
<summary><strong>真实开发 Bug</strong></summary>

工厂方法硬编码 `return Base(...)`，子类调用后仍返回 Base。

**诊断：** 如果希望多态构造，应使用 `@classmethod` 并 `return cls(...)`。

</details>

<details>
<summary><strong>面试追问</strong></summary>

什么时候 staticmethod 比模块级函数更合适？

**回答要点：** 行为概念上属于类命名空间、但不需要实例/类状态时；否则模块函数通常更简单。

</details>


---

<a id="train-061"></a>

### 训练 061 · MRO 与 `super()`

[↩ 返回知识卡 61](Python_碎片化复习手册_CPython_3.14.7.md#card-061)

<details>
<summary><strong>基础题</strong></summary>

多继承中 `super()` 是“调用父类”还是“沿 MRO 调用下一个实现”？

**揭晓：** 后者。

</details>

<details>
<summary><strong>变体题</strong></summary>

为什么 cooperative multiple inheritance 要求链上的方法签名和 `super()` 使用方式互相兼容？

**揭晓：** 每一层都需要把调用继续传递给 MRO 下一节点；某层截断或参数不兼容会破坏整个链。

</details>

<details>
<summary><strong>真实开发 Bug</strong></summary>

一个 mixin 的 `__init__` 没调用 `super().__init__()`，导致 MRO 后面的类初始化逻辑完全没执行。

**诊断：** 在 cooperative hierarchy 中每层都应遵守协作式 `super()` 协议。

</details>

<details>
<summary><strong>面试追问</strong></summary>

为什么不能把 `super()` 简单翻译为“我的父类对象”？

**回答要点：** 它是绑定了当前类型和实例/类的代理，根据 MRO 找下一个实现，并不是某个固定父类实例。

</details>


---

<a id="train-065"></a>

### 训练 065 · Attribute Lookup、`__getattr__` 与 `__getattribute__`

[↩ 返回知识卡 65](Python_碎片化复习手册_CPython_3.14.7.md#card-065)

<details>
<summary><strong>基础题</strong></summary>

`__getattribute__` 与 `__getattr__` 谁更早参与属性访问？

**揭晓：** 所有常规属性访问都先经过 `__getattribute__`；只有正常查找失败时才进入 `__getattr__` fallback。

</details>

<details>
<summary><strong>变体题</strong></summary>

重写 `__getattribute__` 时直接写 `self.__dict__` 为什么容易无限递归？

**揭晓：** 访问 `self.__dict__` 本身也会再次进入 `__getattribute__`；通常委托 `object.__getattribute__(self, name)`。

</details>

<details>
<summary><strong>真实开发 Bug</strong></summary>

代理对象在 `__getattr__` 中对任何缺失属性都返回 `None`，结果拼写错误静默通过。

**诊断：** fallback 太宽；未知属性应适当抛 `AttributeError`，否则破坏调试与 introspection。

</details>

<details>
<summary><strong>面试追问</strong></summary>

Descriptor 在典型 attribute lookup 优先级中的位置？

**回答要点：** data descriptor 通常高于 instance dict；non-data descriptor 通常低于 instance dict；再到 class/MRO 与 `__getattr__` fallback。

</details>


---

<a id="train-066"></a>

### 训练 066 · Descriptor

[↩ 返回知识卡 66](Python_碎片化复习手册_CPython_3.14.7.md#card-066)

<details>
<summary><strong>基础题</strong></summary>

一个只实现 `__get__` 的 descriptor 通常属于 data 还是 non-data descriptor？

**揭晓：** non-data descriptor；若实现 `__set__` 或 `__delete__`，通常成为 data descriptor。

</details>

<details>
<summary><strong>变体题</strong></summary>

为什么普通函数放到 class 上会自动产生 bound method？

**揭晓：** function object 实现 descriptor protocol，访问 `obj.func` 时 `__get__` 完成绑定。

</details>

<details>
<summary><strong>真实开发 Bug</strong></summary>

自定义 descriptor 把值存在 descriptor 实例自己的 `self.value`，结果所有宿主实例共享同一个值。

**诊断：** descriptor 对象本身通常也在 class 上共享；实例数据应按实例存储，例如写入实例 `__dict__` 或外部弱引用映射。

</details>

<details>
<summary><strong>面试追问</strong></summary>

`property` 与 descriptor 是什么关系？

**回答要点：** `property` 本身就是标准库/内建提供的 descriptor，用 descriptor protocol 控制属性读取/写入/删除。

</details>


---

<a id="train-073"></a>

### 训练 073 · Context Manager 与 `with`

[↩ 返回知识卡 73](Python_碎片化复习手册_CPython_3.14.7.md#card-073)

<details>
<summary><strong>基础题</strong></summary>

`with resource:` 最重要的价值是“少写 close()”还是“把获取/释放绑定到控制流退出”？

**揭晓：** 后者；无论正常返回还是异常，都能进入退出逻辑。

</details>

<details>
<summary><strong>变体题</strong></summary>

`__exit__` 返回 truthy 会发生什么？

**揭晓：** 可以表示异常已被处理，从而抑制异常继续传播；这必须非常谨慎。

</details>

<details>
<summary><strong>真实开发 Bug</strong></summary>

数据库 helper 的 `__exit__` 无条件返回 `True`，SQL 异常被吞掉，接口却返回成功。

**诊断：** 不应无差别 suppress exception；通常清理后返回 falsey，让异常继续传播。

</details>

<details>
<summary><strong>面试追问</strong></summary>

Generator-based `@contextmanager` 如何映射到 enter/exit 语义？

**回答要点：** `yield` 前做 acquire，yield 的值对应进入值，yield 后/finally 做 release，异常会被注入 generator。

</details>


---

<a id="train-079"></a>

### 训练 079 · `sys.modules` 与 Import Cache

[↩ 返回知识卡 79](Python_碎片化复习手册_CPython_3.14.7.md#card-079)

<details>
<summary><strong>基础题</strong></summary>

同一进程中第二次 `import mod` 通常会重新完整执行模块顶层代码吗？

**揭晓：** 通常不会；先查 `sys.modules` 中已经加载的 module object。

</details>

<details>
<summary><strong>变体题</strong></summary>

为什么 `importlib.reload(mod)` 也不能简单理解为“恢复一个全新模块进程状态”？

**揭晓：** reload 会重新执行模块代码，但已有外部引用、对象实例、from-import 绑定等可能仍指向旧对象。

</details>

<details>
<summary><strong>真实开发 Bug</strong></summary>

单测修改模块级全局缓存后，后续测试重新 `import` 以为能恢复初始状态，结果缓存还在。

**诊断：** import cache 复用同一模块对象；测试需要显式 reset 状态、隔离进程或谨慎 reload。

</details>

<details>
<summary><strong>面试追问</strong></summary>

循环导入为什么常出现“partially initialized module”？

**回答要点：** 模块执行完成前就已注册进 `sys.modules`，另一模块可拿到这个尚未定义完全部名称的对象。

</details>


---

<a id="train-080"></a>

### 训练 080 · Circular Import

[↩ 返回知识卡 80](Python_碎片化复习手册_CPython_3.14.7.md#card-080)

<details>
<summary><strong>基础题</strong></summary>

A import B，B 又 `from A import x`，但 A 尚未执行到 `x = ...`。为什么失败？

**揭晓：** B 看到的是 A 的半初始化 module object，`x` 尚不存在。

</details>

<details>
<summary><strong>变体题</strong></summary>

把 import 移进函数为什么“可能修复”但不一定是最佳设计？

**揭晓：** 它推迟导入时机，可能绕开初始化环；但真实问题往往是模块依赖方向/职责耦合。

</details>

<details>
<summary><strong>真实开发 Bug</strong></summary>

`models.py` import `services.py`，`services.py` 又 import `models.py`，随着功能增长不断出现循环。

**诊断：** 抽取共享协议/类型/常量到下层模块，重新设计依赖方向，而不是继续散布局部 import。

</details>

<details>
<summary><strong>面试追问</strong></summary>

`import module` 与 `from module import name` 在循环导入下哪个更容易受“名称尚未创建”影响？

**回答要点：** 后者需要导入当下立即解析具体名称，因此更直接暴露半初始化问题；前者可晚些通过 module 访问。

</details>


---

<a id="train-082"></a>

### 训练 082 · Type Hint 不改变动态类型本质

[↩ 返回知识卡 82](Python_碎片化复习手册_CPython_3.14.7.md#card-082)

<details>
<summary><strong>基础题</strong></summary>

```python
def f(x: int) -> int:
    return x

print(f("hello"))
```

在没有额外 runtime validator 时会怎样？

**揭晓：** 正常返回字符串；annotation 本身通常不强制运行时类型。

</details>

<details>
<summary><strong>变体题</strong></summary>

既然运行时不强制，Type Hint 有什么实际价值？

**揭晓：** 静态检查、IDE、重构、文档、API 合约表达，以及供框架/工具选择性读取 metadata。

</details>

<details>
<summary><strong>真实开发 Bug</strong></summary>

团队以为 `user_id: int` 会自动拒绝 HTTP 字符串输入，实际运行到数据库层才出错。

**诊断：** typing 不是 validation；输入边界仍需解析与校验。

</details>

<details>
<summary><strong>面试追问</strong></summary>

Structural typing/Protocol 和继承式 nominal typing 的差别是什么？

**回答要点：** Protocol 可按对象具备的结构/能力静态匹配，不要求显式继承指定基类。

</details>


---

<a id="train-106"></a>

### 训练 106 · Concurrency 与 Parallelism

[↩ 返回知识卡 106](Python_碎片化复习手册_CPython_3.14.7.md#card-106)

<details>
<summary><strong>基础题</strong></summary>

单核 CPU 上两个任务交替推进，可以叫 concurrency 吗？可以叫真正同时 parallelism 吗？

**揭晓：** 可以并发；不一定并行。

</details>

<details>
<summary><strong>变体题</strong></summary>

异步 IO 为什么主要解决“等待期间让出执行权”，而不是让 CPU 密集计算变快？

**揭晓：** event loop 依赖协作切换；CPU 长计算不 await 就会持续占用线程。

</details>

<details>
<summary><strong>真实开发 Bug</strong></summary>

团队把图像压缩 CPU 任务改成 `async def`，吞吐没有提升反而卡住整个服务。

**诊断：** 仅加 async 不会把 CPU 工作变并行；考虑进程池、原生扩展或真正并行运行方式。

</details>

<details>
<summary><strong>面试追问</strong></summary>

选择 thread/process/asyncio 的第一判断维度有哪些？

**回答要点：** CPU-bound/IO-bound、共享内存需求、隔离需求、库是否阻塞、任务规模与调度开销。

</details>


---

<a id="train-110"></a>

### 训练 110 · Race Condition 与 Lock

[↩ 返回知识卡 110](Python_碎片化复习手册_CPython_3.14.7.md#card-110)

<details>
<summary><strong>基础题</strong></summary>

“检查余额足够 → 扣减余额”由多条操作组成，即使单条操作各自安全，整体为什么仍会竞态？

**揭晓：** check-then-act 不是原子事务；其他执行单元可在中间改变共享状态。

</details>

<details>
<summary><strong>变体题</strong></summary>

加了 Lock 是否就自动避免所有并发问题？

**揭晓：** 不会；锁粒度、锁顺序、覆盖的数据不一致、死锁、跨进程状态等仍需设计。

</details>

<details>
<summary><strong>真实开发 Bug</strong></summary>

两个线程都执行 `if key not in cache: cache[key] = expensive()`，昂贵任务被执行两次。

**诊断：** 复合操作存在 race；需要用锁保护整个检查/创建临界区或使用并发安全的 single-flight 设计。

</details>

<details>
<summary><strong>面试追问</strong></summary>

为什么 GIL 不等于你的 Python 业务逻辑“线程安全”？

**回答要点：** GIL 控制解释器层执行权，不保证多条 Python 操作构成的业务不变量原子，也会在 IO/扩展等处切换。

</details>


---

<a id="train-113"></a>

### 训练 113 · 传统 GIL 心智模型

[↩ 返回知识卡 113](Python_碎片化复习手册_CPython_3.14.7.md#card-113)

<details>
<summary><strong>基础题</strong></summary>

传统 GIL-enabled CPython 中，同一解释器内多个线程能否同时执行 Python bytecode？

**揭晓：** 通常一个时刻只有持有 GIL 的线程执行 Python bytecode。

</details>

<details>
<summary><strong>变体题</strong></summary>

为什么 I/O-bound 多线程仍可能明显提速？

**揭晓：** 阻塞 IO/很多 C API 可释放 GIL，其他线程可以在等待期间推进。

</details>

<details>
<summary><strong>真实开发 Bug</strong></summary>

CPU 密集纯 Python 任务从单线程改 8 线程，CPU 满但总体耗时没下降。

**诊断：** 传统 GIL 下线程不提供这种 Python bytecode CPU parallelism；考虑 process/free-threaded/释放 GIL 的原生实现。

</details>

<details>
<summary><strong>面试追问</strong></summary>

GIL 最容易被误解成哪三件事？

**回答要点：** “没有线程”“所有代码线程安全”“Python 永远不能并行”都不准确。

</details>


---

<a id="train-114"></a>

### 训练 114 · Free-threaded CPython 3.14

[↩ 返回知识卡 114](Python_碎片化复习手册_CPython_3.14.7.md#card-114)

<details>
<summary><strong>基础题</strong></summary>

Free-threaded build 的目标是什么？

**揭晓：** 允许 CPython 在不依赖传统全局 GIL 的构建中让多个线程并行执行 Python 代码，同时通过新的运行时机制维护对象安全。

</details>

<details>
<summary><strong>变体题</strong></summary>

“没有 GIL”是否意味着现有多线程代码无需锁？

**揭晓：** 恰恰相反；业务共享可变状态的 race 更需要显式同步。解释器保证内部结构安全，不等于你的不变量安全。

</details>

<details>
<summary><strong>真实开发 Bug</strong></summary>

迁移 free-threaded 后，原来“靠 GIL 碰巧没出问题”的共享 dict 读改写开始出现业务竞态。

**诊断：** 把隐式时序假设改成明确同步/消息传递；同时检查 C extension 的兼容性与是否会重新启用 GIL。

</details>

<details>
<summary><strong>面试追问</strong></summary>

为什么 free-threaded 不能简单说成“把 GIL 删除了就完事”？

**回答要点：** 需要替代引用计数、容器内部同步、内存分配等机制；性能、扩展兼容和对象生命周期行为也会变化。

</details>


---

<a id="train-115"></a>

### 训练 115 · Asyncio 核心模型

[↩ 返回知识卡 115](Python_碎片化复习手册_CPython_3.14.7.md#card-115)

<details>
<summary><strong>基础题</strong></summary>

Event loop 的核心职责是什么？

**揭晓：** 管理可运行 Task、等待 IO/定时事件，在 coroutine 明确让出执行权后调度其他任务。

</details>

<details>
<summary><strong>变体题</strong></summary>

创建很多 coroutine object 是否等于它们已经并发运行？

**揭晓：** 不等于；需要被 await 或包装成 Task/交给 event loop 调度。

</details>

<details>
<summary><strong>真实开发 Bug</strong></summary>

代码写 `fetch(url)` 得到 coroutine object 后没 await，程序结束时报 “coroutine was never awaited”。

**诊断：** 创建了 coroutine 但没有驱动执行；明确 await 或创建/管理 Task。

</details>

<details>
<summary><strong>面试追问</strong></summary>

Asyncio 的并发为什么叫 cooperative scheduling？

**回答要点：** coroutine 必须在 await 等点主动让出控制权；一个不让出的任务会占住 event-loop thread。

</details>


---

<a id="train-117"></a>

### 训练 117 · Task、TaskGroup 与结构化并发

[↩ 返回知识卡 117](Python_碎片化复习手册_CPython_3.14.7.md#card-117)

<details>
<summary><strong>基础题</strong></summary>

Coroutine 与 Task 的核心区别？

**揭晓：** coroutine 是可等待计算对象；Task 把 coroutine 注册到 event loop，代表被调度执行及其状态/结果。

</details>

<details>
<summary><strong>变体题</strong></summary>

为什么“创建 Task 后完全不保存/管理它”容易产生 orphan/background task 问题？

**揭晓：** 生命周期、异常、取消和 shutdown 边界不清楚；错误可能延迟暴露或任务被提前终止。

</details>

<details>
<summary><strong>真实开发 Bug</strong></summary>

请求里 `create_task(write_audit())` 后立刻返回；进程 shutdown 时审计任务还没完成，也没人处理异常。

**诊断：** 若任务属于请求作用域，应使用结构化并发/TaskGroup 等明确等待与取消边界；真正后台任务需要独立生命周期管理。

</details>

<details>
<summary><strong>面试追问</strong></summary>

TaskGroup 相比随手 `create_task()` 的核心设计收益？

**回答要点：** 子任务生命周期被绑定到一个明确 scope，异常传播、等待和取消更可推理。

</details>


---

<a id="train-119"></a>

### 训练 119 · Blocking Code 会卡 Event Loop

[↩ 返回知识卡 119](Python_碎片化复习手册_CPython_3.14.7.md#card-119)

<details>
<summary><strong>基础题</strong></summary>

```python
async def handler():
    import time
    time.sleep(5)
```

为什么这 5 秒会拖住同一 event loop 的其他任务？

**揭晓：** `time.sleep` 阻塞 event-loop 所在线程，没有 `await` 让出控制权。

</details>

<details>
<summary><strong>变体题</strong></summary>

把阻塞函数外面套一层 `async def` 会自动变成异步吗？

**揭晓：** 不会；内部依然同步占用线程。

</details>

<details>
<summary><strong>真实开发 Bug</strong></summary>

异步接口中直接调用同步 SDK，偶发 2 秒网络等待时所有请求延迟一起飙升。

**诊断：** 换原生 async SDK，或把合适的阻塞 IO 移到 `asyncio.to_thread()`/线程池，并评估线程安全。

</details>

<details>
<summary><strong>面试追问</strong></summary>

`to_thread()` 适合解决 CPU-bound 吗？

**回答要点：** 主要用于不会释放 event loop 的阻塞同步 IO；传统 GIL 下纯 Python CPU-bound 不会因此获得理想并行。

</details>


---

<a id="train-124"></a>

### 训练 124 · Reference Counting

[↩ 返回知识卡 124](Python_碎片化复习手册_CPython_3.14.7.md#card-124)

<details>
<summary><strong>基础题</strong></summary>

默认 GIL-enabled CPython 中，对象引用计数降为零通常意味着什么？

**揭晓：** 通常可以立即进入对象销毁/释放流程；但这是 CPython 实现模型，不是 Python 语言保证。

</details>

<details>
<summary><strong>变体题</strong></summary>

为什么不能把这条规则原封不动套到 free-threaded CPython 3.14？

**揭晓：** free-threaded 引入 biased/deferred 等引用计数机制和 immortal object 等优化，对象最终回收时机可不同。

</details>

<details>
<summary><strong>真实开发 Bug</strong></summary>

代码依赖 `del obj` 后“马上执行 finalizer 释放外部资源”，换实现/运行模式后行为变得不可靠。

**诊断：** 资源释放不应依赖 GC/refcount 时机；用 context manager/显式 close。

</details>

<details>
<summary><strong>面试追问</strong></summary>

引用计数最大的优点和经典缺陷？

**回答要点：** 生命周期通常及时且直观；循环引用不能靠计数归零自行解决。

</details>


---

<a id="train-125"></a>

### 训练 125 · 循环引用与 Cyclic GC

[↩ 返回知识卡 125](Python_碎片化复习手册_CPython_3.14.7.md#card-125)

<details>
<summary><strong>基础题</strong></summary>

两个对象只互相引用、没有外部可达引用时，为什么 refcount 可能都不为零？

**揭晓：** 它们彼此贡献引用，因此需要 cyclic GC 判断这组对象从根不可达。

</details>

<details>
<summary><strong>变体题</strong></summary>

“Python 有 GC，所以引用计数不重要”为什么不对？

**揭晓：** CPython 常规对象生命周期主要仍高度依赖引用计数；cyclic GC 主要补充处理可参与循环的容器对象图。

</details>

<details>
<summary><strong>真实开发 Bug</strong></summary>

长期服务中大量对象互相注册回调形成环，虽最终可 GC，但高峰内存持续很久。

**诊断：** 可考虑打断无必要强引用、使用 weakref、显式注销，不能只说“GC 最后会收”。

</details>

<details>
<summary><strong>面试追问</strong></summary>

CPython 3.14 的 GC 分代模型和旧教程相比为什么需要核对版本？

**回答要点：** 3.14 调整了 GC generation 结构/行为；涉及代数、阈值和调优时必须以目标版本官方文档为准。

</details>


---

<a id="train-144"></a>

### 训练 144 · `subprocess`

[↩ 返回知识卡 144](Python_碎片化复习手册_CPython_3.14.7.md#card-144)

<details>
<summary><strong>基础题</strong></summary>

运行固定命令时为什么通常优先：

```python
subprocess.run(["git", "status"], check=True)
```

而不是拼 shell 字符串？

**揭晓：** argv 边界清楚，少一层 shell 解析，也更容易避免 injection。

</details>

<details>
<summary><strong>变体题</strong></summary>

`check=True` 解决的是 timeout 吗？

**揭晓：** 不是；它在子进程非零 exit code 时抛异常。超时需单独 `timeout=`。

</details>

<details>
<summary><strong>真实开发 Bug</strong></summary>

服务调用 CLI，不读 stderr 也不检查 returncode，只根据 stdout 是否为空判断成功，导致错误被当成“无结果”。

**诊断：** 明确 exit code、stdout、stderr、timeout，并按协议解释结果。

</details>

<details>
<summary><strong>面试追问</strong></summary>

什么时候确实需要 `shell=True`？使用时应增加哪些边界？

**回答要点：** 需要 shell syntax/管道/内建命令时；输入应完全受控或严格设计，不把不可信数据拼进命令。

</details>


---

<a id="train-148"></a>

### 训练 148 · Timeout、Retry 与 Idempotency

[↩ 返回知识卡 148](Python_碎片化复习手册_CPython_3.14.7.md#card-148)

<details>
<summary><strong>基础题</strong></summary>

为什么网络调用“有 retry 却没有 timeout”仍然可能永久卡住？

**揭晓：** 单次尝试没有失败边界，重试逻辑根本得不到执行机会。

</details>

<details>
<summary><strong>变体题</strong></summary>

HTTP POST 失败后是否一定可以安全重试？

**揭晓：** 不一定；如果服务端已处理但响应丢失，重试可能重复产生副作用。需考虑幂等语义/幂等键。

</details>

<details>
<summary><strong>真实开发 Bug</strong></summary>

支付请求客户端 timeout 后自动重试 3 次，最终用户被扣款两次。

**诊断：** timeout 不代表远端没执行；支付写操作需要 idempotency key、服务端去重和明确重试策略。

</details>

<details>
<summary><strong>面试追问</strong></summary>

设计 retry policy 至少考虑哪些维度？

**回答要点：** 哪些错误可重试、幂等性、最大次数/总时限、指数退避与 jitter、上下游容量及可观测性。

</details>


---

<a id="train-150"></a>

### 训练 150 · Transaction

[↩ 返回知识卡 150](Python_碎片化复习手册_CPython_3.14.7.md#card-150)

<details>
<summary><strong>基础题</strong></summary>

三个相关 UPDATE 中第二个成功、第三个失败。transaction 的目标是什么？

**揭晓：** 把这组操作作为原子业务单元；失败时 rollback，避免只提交一半。

</details>

<details>
<summary><strong>变体题</strong></summary>

transaction 是否等于“拿一个全局锁，让其他事务都不能运行”？

**揭晓：** 不是；并发与隔离由数据库隔离级别/MVCC/锁机制等共同决定。

</details>

<details>
<summary><strong>真实开发 Bug</strong></summary>

函数内部先 `commit()`，然后再写审计表；审计失败后 rollback 已无法撤回前面的业务更新。

**诊断：** transaction boundary 切错；属于同一原子业务约束的写操作应放在同一事务边界内。

</details>

<details>
<summary><strong>面试追问</strong></summary>

应用层 transaction 和 connection pool 分别解决什么问题？

**回答要点：** transaction 管原子性/一致性边界；pool 管连接建立成本与复用，两者不是替代关系。

</details>


---

<a id="train-156"></a>

### 训练 156 · Fixture

[↩ 返回知识卡 156](Python_碎片化复习手册_CPython_3.14.7.md#card-156)

<details>
<summary><strong>基础题</strong></summary>

pytest fixture 最核心的价值是“少写几行 setup”吗？

**揭晓：** 更重要的是声明测试资源/依赖及其生命周期，并可组合 setup/teardown。

</details>

<details>
<summary><strong>变体题</strong></summary>

把可变 fixture 提升到 session scope 为什么可能制造测试相互污染？

**揭晓：** 多个测试共享同一对象状态；scope 越大，隔离责任越重。

</details>

<details>
<summary><strong>真实开发 Bug</strong></summary>

一个 session-scoped `client` fixture 在测试中修改默认 headers，后面的无关测试突然携带认证头。

**诊断：** 共享可变状态；降低 scope、每次重建，或在 teardown/测试前明确 reset。

</details>

<details>
<summary><strong>面试追问</strong></summary>

Fixture 与 helper function 的本质差异？

**回答要点：** fixture 进入测试框架的依赖解析、scope、teardown 与参数化模型；helper 只是普通函数调用。

</details>


---

<a id="train-158"></a>

### 训练 158 · Dummy、Stub、Spy、Mock、Fake

[↩ 返回知识卡 158](Python_碎片化复习手册_CPython_3.14.7.md#card-158)

<details>
<summary><strong>基础题</strong></summary>

只需要返回固定 API 响应，不关心调用次数，最接近 Stub 还是 Mock？

**揭晓：** Stub。

</details>

<details>
<summary><strong>变体题</strong></summary>

“Mock”为什么不应该成为所有 test double 的统称？

**揭晓：** 不同 double 关注点不同：Stub 提供结果、Spy 记录、Mock 验证交互、Fake 提供简化可运行实现等。

</details>

<details>
<summary><strong>真实开发 Bug</strong></summary>

单测把内部每个私有函数都 mock 掉，重构内部调用顺序后业务行为没变却几十个测试失败。

**诊断：** 测试过度绑定 implementation detail；应围绕可观察行为和真正的外部边界替换依赖。

</details>

<details>
<summary><strong>面试追问</strong></summary>

什么时候 Fake 比 Mock 更有价值？

**回答要点：** 当需要一个轻量但行为真实的替代实现（如 in-memory repository）来测试更完整协作，而不是只验证调用。

</details>


---

<a id="train-159"></a>

### 训练 159 · Patch 的位置

[↩ 返回知识卡 159](Python_碎片化复习手册_CPython_3.14.7.md#card-159)

<details>
<summary><strong>基础题</strong></summary>

```python
# service.py
from client import send

def work():
    return send()
```

测试 `work()` 时通常 patch `client.send` 还是 `service.send`？

**揭晓：** `service.send`，因为被测代码在自己的 namespace 查找这个绑定。

</details>

<details>
<summary><strong>变体题</strong></summary>

为什么 patch 原始定义位置有时“看起来成功”，有时完全没效果？

**揭晓：** 取决于被测模块持有的是动态 module attribute lookup 还是已经复制绑定的 `from ... import name`。

</details>

<details>
<summary><strong>真实开发 Bug</strong></summary>

测试 patch `requests.get`，但模块顶层早已 `from requests import get`；测试仍发真实网络请求。

**诊断：** patch where looked up，即被测模块中的 `get` 名字。

</details>

<details>
<summary><strong>面试追问</strong></summary>

Patch 位置问题和 Python 名称绑定模型有什么直接关系？

**回答要点：** import 会在目标 namespace 建立绑定；函数运行时按该 namespace 查名称，不会自动回到最初定义模块。

</details>


---

<a id="train-166"></a>

### 训练 166 · Big O 与 Python 容器复杂度

[↩ 返回知识卡 166](Python_碎片化复习手册_CPython_3.14.7.md#card-166)

<details>
<summary><strong>基础题</strong></summary>

对 10 万元素做 10 万次 membership，为什么 `set` 往往比 `list` 合适？

**揭晓：** list membership 平均线性 O(n)，set 平均 O(1) 哈希查找；一次建 set 的 O(n) 可被大量查询摊薄。

</details>

<details>
<summary><strong>变体题</strong></summary>

O(1) 是否意味着“永远只花 1 纳秒”？

**揭晓：** Big O 描述增长趋势，不给出常数，也不排除哈希冲突、缓存局部性、对象 hash 成本等现实因素。

</details>

<details>
<summary><strong>真实开发 Bug</strong></summary>

接口为每条订单都用 `if user_id in user_list` 在线性 list 查 5 万用户，总体变成隐藏 O(n²)。

**诊断：** 预构建 `set(user_ids)` 或 dict index，把重复查找变成平均 O(1)。

</details>

<details>
<summary><strong>面试追问</strong></summary>

复杂度优化和 micro benchmark 谁应先？

**回答要点：** 先确认算法/数据结构数量级，再用 profiling/benchmark 验证热点和实际常数。

</details>


---

<a id="train-169"></a>

### 训练 169 · Cache

[↩ 返回知识卡 169](Python_碎片化复习手册_CPython_3.14.7.md#card-169)

<details>
<summary><strong>基础题</strong></summary>

缓存为什么不仅是“加一个 dict”？至少还要考虑哪两个问题？

**揭晓：** key 设计、失效/一致性、容量/淘汰、并发、命中率、错误缓存等。

</details>

<details>
<summary><strong>变体题</strong></summary>

`functools.lru_cache` 用在有副作用函数上有什么风险？

**揭晓：** 后续相同参数可能直接返回旧结果而跳过副作用；缓存要求函数语义适合按输入复用结果。

</details>

<details>
<summary><strong>真实开发 Bug</strong></summary>

权限查询缓存 1 小时，用户被撤权后仍能访问。

**诊断：** 缓存失效策略不满足安全一致性要求；缩短 TTL、事件驱动失效，或对高风险决策避免过强缓存。

</details>

<details>
<summary><strong>面试追问</strong></summary>

Cache stampede 是什么？

**回答要点：** 热 key 同时过期，大量请求并发回源；可用 single-flight、随机 TTL、提前刷新等控制。

</details>


---

<a id="train-170"></a>

### 训练 170 · N+1、批处理与 IO 性能

[↩ 返回知识卡 170](Python_碎片化复习手册_CPython_3.14.7.md#card-170)

<details>
<summary><strong>基础题</strong></summary>

查询 100 个订单后，再为每个订单单独查一次用户，一共大约多少次数据库 round trip？

**揭晓：** 1 + 100 = 101 次，这就是典型 N+1 形态。

</details>

<details>
<summary><strong>变体题</strong></summary>

把 101 次查询改成“并发发 101 次”是否等价于解决 N+1？

**揭晓：** 不等价；可能降低单请求墙钟时间，却放大数据库连接/负载。应优先减少 round trip。

</details>

<details>
<summary><strong>真实开发 Bug</strong></summary>

ORM 页面本地测试很快，线上 1000 条结果导致 1001 次 SQL，连接池被打满。

**诊断：** eager loading/join/batch IN query 等减少访问次数，并用 tracing/query count 监测。

</details>

<details>
<summary><strong>面试追问</strong></summary>

为什么 IO 性能常应先优化“次数”而不是 Python 循环本身？

**回答要点：** 网络/数据库 round trip 延迟通常远大于本地微秒级操作，减少边界穿越收益更大。

</details>


---

<a id="train-174"></a>

### 训练 174 · `pyproject.toml`

[↩ 返回知识卡 174](Python_碎片化复习手册_CPython_3.14.7.md#card-174)

<details>
<summary><strong>基础题</strong></summary>

`pyproject.toml` 在现代 Python 项目中可承载哪些类别的信息？

**揭晓：** build system、project metadata，以及很多工具的统一配置；具体字段受相应规范/工具约定约束。

</details>

<details>
<summary><strong>变体题</strong></summary>

存在 `pyproject.toml` 是否意味着项目一定使用某个固定构建工具？

**揭晓：** 不是；它是标准配置入口，可声明不同 build backend，也可只承载工具配置。

</details>

<details>
<summary><strong>真实开发 Bug</strong></summary>

项目只在开发者电脑可安装，CI 构建报缺 build backend，原因是本地环境“碰巧装过”依赖却没在 build-system 中声明。

**诊断：** 构建环境必须可从项目元数据独立复现，不依赖全局环境偶然状态。

</details>

<details>
<summary><strong>面试追问</strong></summary>

`[build-system]` 和 `[project]` 解决的问题分别是什么？

**回答要点：** 前者描述构建需要什么 backend/requirements；后者描述 distribution 的标准项目元数据。

</details>


---

<a id="train-175"></a>

### 训练 175 · Wheel 与 Source Distribution

[↩ 返回知识卡 175](Python_碎片化复习手册_CPython_3.14.7.md#card-175)

<details>
<summary><strong>基础题</strong></summary>

Wheel 和 sdist 最核心区别是什么？

**揭晓：** wheel 是构建好的 distribution 格式，安装通常无需再执行项目构建；sdist 是源码分发，需要目标环境构建。

</details>

<details>
<summary><strong>变体题</strong></summary>

为什么含原生扩展的 wheel 常带平台/ABI 标签，而纯 Python wheel 更通用？

**揭晓：** 编译产物依赖 OS/架构/Python ABI；纯 Python 文件没有这种二进制兼容限制。

</details>

<details>
<summary><strong>真实开发 Bug</strong></summary>

生产环境离线安装包只有 sdist，却没有编译器和系统头文件，部署失败。

**诊断：** 部署前构建并验证目标平台可用 wheel，或准备完整构建工具链。

</details>

<details>
<summary><strong>面试追问</strong></summary>

`pip install` 一个 sdist 时“安装”为什么可能实际包含构建步骤？

**回答要点：** installer 会调用声明的 build backend 构建 wheel/产物，再安装到环境。

</details>


---

<a id="train-181"></a>

### 训练 181 · `eval()` 与 `exec()`

[↩ 返回知识卡 181](Python_碎片化复习手册_CPython_3.14.7.md#card-181)

<details>
<summary><strong>基础题</strong></summary>

把用户输入传给 `eval()` 的核心风险是什么？

**揭晓：** 把“不可信数据”提升成“可执行 Python 代码”。

</details>

<details>
<summary><strong>变体题</strong></summary>

只把 `globals={"__builtins__": {}}` 传给 eval 是否就得到可靠安全沙箱？

**揭晓：** 不应这样假设；Python 对象模型可提供复杂逃逸路径，`eval` 不是为安全沙箱设计的。

</details>

<details>
<summary><strong>真实开发 Bug</strong></summary>

配置系统允许管理员输入公式并直接 `eval(formula)`，后来权限边界变化，低权限用户也能编辑公式。

**诊断：** 不应依赖“现在只有可信人会输入”；改为解析受限表达式 DSL/AST 白名单或明确支持的运算集合。

</details>

<details>
<summary><strong>面试追问</strong></summary>

什么时候 `ast.literal_eval` 比 `eval` 合适？它又不解决什么？

**回答要点：** 只需解析 Python literal 结构时更安全；它不是通用表达式执行器，也仍需考虑输入大小/资源耗尽。

</details>


---

<a id="train-182"></a>

### 训练 182 · Command Injection

[↩ 返回知识卡 182](Python_碎片化复习手册_CPython_3.14.7.md#card-182)

<details>
<summary><strong>基础题</strong></summary>

为什么：

```python
subprocess.run(["grep", user_text, path])
```

通常比 `shell=True` 拼接字符串更安全？

**揭晓：** 参数以 argv 边界传给程序，不再由 shell 把用户内容解释为 `;`, `&&`, `$()` 等控制语法。

</details>

<details>
<summary><strong>变体题</strong></summary>

使用 argv list 是否意味着所有输入风险都消失？

**揭晓：** 没有；目标程序自己的 option injection、路径、资源、语义约束仍需验证，例如用户值以 `-` 开头可能被解释成选项。

</details>

<details>
<summary><strong>真实开发 Bug</strong></summary>

图片转换服务执行 `convert "{filename}" out.png`，攻击者上传文件名 `x.jpg; curl ...`。

**诊断：** shell command injection；不要拼 shell，使用 argv、受控路径和目标工具自身的安全选项。

</details>

<details>
<summary><strong>面试追问</strong></summary>

Command injection 与 SQL injection 有什么共同抽象？

**回答要点：** 都把不可信数据混入了指令语法；解决核心是让“数据”和“代码/命令结构”保持分离。

</details>


---

<a id="train-185"></a>

### 训练 185 · `random` vs `secrets`

[↩ 返回知识卡 185](Python_碎片化复习手册_CPython_3.14.7.md#card-185)

<details>
<summary><strong>基础题</strong></summary>

生成密码重置 token 应用 `random` 还是 `secrets`？

**揭晓：** `secrets`。

</details>

<details>
<summary><strong>变体题</strong></summary>

给 `random.seed()` 一个“看起来很随机”的当前时间，是否适合安全 token？

**揭晓：** 不适合；Mersenne Twister 面向模拟/统计，输出可预测，不提供密码学安全保证。

</details>

<details>
<summary><strong>真实开发 Bug</strong></summary>

邀请码使用 `random.randint(100000, 999999)`，攻击者可大量枚举且可能推断序列。

**诊断：** 如果邀请码承担认证/授权秘密，应提高熵并使用 `secrets`，同时结合速率限制/过期等机制。

</details>

<details>
<summary><strong>面试追问</strong></summary>

`secrets` 解决了 token 系统的全部安全问题吗？

**回答要点：** 只解决安全随机来源；还需长度/熵、存储、过期、单次使用、比较、防暴力破解等设计。

</details>


---

<a id="train-189"></a>

### 训练 189 · EAFP 与 LBYL

[↩ 返回知识卡 189](Python_碎片化复习手册_CPython_3.14.7.md#card-189)

<details>
<summary><strong>基础题</strong></summary>

访问 dict 可选 key 时，`try/except KeyError` 与先 `if key in d` 各体现什么风格？

**揭晓：** 前者偏 EAFP；后者偏 LBYL。

</details>

<details>
<summary><strong>变体题</strong></summary>

EAFP 是否意味着 `except Exception: pass`？

**揭晓：** 完全不是；EAFP 应捕获**预期且具体**的异常，并保持失败可解释。

</details>

<details>
<summary><strong>真实开发 Bug</strong></summary>

先 `if path.exists(): path.open()`，两个操作之间文件被删除，仍报错。

**诊断：** 典型 TOCTOU；在不可原子保证的环境里，直接尝试 open 并处理具体异常通常更可靠。

</details>

<details>
<summary><strong>面试追问</strong></summary>

什么时候 LBYL 更合理？

**回答要点：** 检查本身就是业务规则、能避免昂贵/不可逆副作用，或 API 提供稳定且无竞争窗口的预检时。

</details>


---

<a id="train-190"></a>

### 训练 190 · Pure Function 与 Side Effect

[↩ 返回知识卡 190](Python_碎片化复习手册_CPython_3.14.7.md#card-190)

<details>
<summary><strong>基础题</strong></summary>

“相同输入总得到相同输出且不修改外部状态”描述的是什么倾向？

**揭晓：** Pure function。

</details>

<details>
<summary><strong>变体题</strong></summary>

读取当前时间的函数没有写文件，为什么仍不算纯函数？

**揭晓：** 结果依赖隐式外部状态（时间），相同显式输入不保证相同输出。

</details>

<details>
<summary><strong>真实开发 Bug</strong></summary>

`calculate_price(order)` 内部偷偷读取全局促销配置和当前时间，单测在不同日期随机失败。

**诊断：** 把隐式依赖显式参数化（规则、clock），纯计算与 IO/状态读取分层。

</details>

<details>
<summary><strong>面试追问</strong></summary>

为什么纯函数常更容易缓存、并行和测试？

**回答要点：** 输入输出关系稳定，缺少共享副作用，结果可重现，依赖边界明确。

</details>


---

<a id="train-192"></a>

### 训练 192 · Composition vs Inheritance

[↩ 返回知识卡 192](Python_碎片化复习手册_CPython_3.14.7.md#card-192)

<details>
<summary><strong>基础题</strong></summary>

“Car has an Engine” 更自然倾向 composition 还是 inheritance？

**揭晓：** composition。

</details>

<details>
<summary><strong>变体题</strong></summary>

为了复用 5 行代码就继承一个基类，为什么可能是坏设计？

**揭晓：** inheritance 建立的是更强的 is-a、替换性和生命周期耦合，不只是代码复用工具。

</details>

<details>
<summary><strong>真实开发 Bug</strong></summary>

业务类继承某 SDK Client 只为复用 `request()`，后来 SDK 增加初始化约束和方法名冲突，业务类被迫跟着变化。

**诊断：** 依赖能力而非“成为 SDK Client”；把 client 作为组合依赖注入更稳。

</details>

<details>
<summary><strong>面试追问</strong></summary>

什么时候 inheritance 反而是合适的？

**回答要点：** 存在稳定的 is-a 关系、基类明确设计为扩展点、子类能遵守替换契约，并且 MRO/生命周期可控。

</details>


<a id="chapter-31"></a>

<a id="v10-review"></a>
