Files
MCM-ICM_Toolkit/docs/dev/personal-pitfalls.md
T
guzhujushi 49fd84a2df docs: L0 进度 3/12,记录 zstandard 残骸坑与决策 26/27
- STATUS.md:本次任务拆成「第一阶段已完成 / 第二阶段进行中」;
  L0 3/12;测试计数改为 39 passed / 44 skipped;
  下一步里写明 io.py 待修的两处(mkdir 早于校验、缺 Raises)
- docs/04-environment.md 坑 6:conda 升级留下的 *.conda_trash 残骸目录会被
  Python 当成 namespace package 导入成功(__file__ 为 None),
  pandas 3.0 因此抛 ImportError: Can't determine version for zstandard
  直接打断 pd.read_csv;含排查方法与「改名禁用」的修法
- docs/dev/personal-pitfalls.md:条目 3(注解与 docstring 打架)复发 +1
- 决策 26:未实现的算法逐条 skip,禁用模块级 pytestmark
- 决策 27:zstandard 残骸改名禁用,不删不重装
- CHANGELOG:补 load_table/save_table 与 Fixed 段
2026-10-01 23:11:48 +08:00

69 lines
3.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 个人易错点(只追加)
> **本文件的规矩**
> 1. **只追加。** 已有条目一个字都不改、不重排、不润色。发现写错了 → 在末尾补一条"更正",不要回头改。
> 2. **不通读。** 追加前只做两件事:`rg -i "<关键词>" docs/dev/personal-pitfalls.md` 看有没有记过;
> 看一眼下面的索引表最后一行拿编号。全文只在复盘时才读。
> 3. 一条写三行:**现象** / **为什么会错** / **以后怎么防**。
> 写不出"为什么会错"就先别记——那说明还没想清楚。
> 4. 记**规律**,不记事故。"今天又忘了"不新开条目,在已有条目下把 `复发` 加一。
## 追加模板
```markdown
### N. <一句话>
- 现象:
- 为什么会错:
- 以后怎么防:
- 首次:YYYY-MM-DD|复发:0
```
## 索引
| # | 一句话 | 首次 |
|---|---|---|
| 1 | 类型判断只写 `int`,漏掉 `np.int64` 这类 numpy 标量 | 2026-10-01 |
| 2 | 以为 `# TODO(L0)` 只是普通注释 | 2026-10-01 |
| 3 | 签名里的类型注解和 docstring 说的不一致 | 2026-10-01 |
| 4 | 只覆盖自己的状态,忘了第三方库还在用 numpy 旧全局 | 2026-10-01 |
---
### 1. 类型判断只写 `int`,漏掉 `np.int64` 这类 numpy 标量
- 现象:`get_rng(np.int64(42))` 不报错,但种子被静默忽略、退回全局流——结果不可复现,且没有任何提示。
- 为什么会错:`np.int64` **不是** python `int` 的子类(`isinstance(np.int64(42), int)` 是 `False`),
但 `np.float64` **是** `float` 的子类。numpy 标量的继承关系不统一,凭直觉一定会翻车。
- 以后怎么防:写类型分支时默认假设"这个值可能来自 numpy"。判断用 `isinstance(x, (int, np.integer))`,
取值统一 `int(x)` 归一化;并且给所有非预期类型加 `raise TypeError` 兜底——
**宁可报错,也不要静默走默认分支**。
- 首次:2026-10-01|复发:0
### 2. 以为 `# TODO(L0)` 只是普通注释
- 现象:函数实现完了,`# TODO(L0)` 还挂在末尾。
- 为什么会错:本仓库把 `TODO(L0)` / `TODO(L1)` / `TODO(v0.2)` 当**进度标记**用:
`docs/03-roadmap.md` 靠它划分档位,`STATUS.md` 靠它统计"还剩哪些没做"。实现完不删,等于骗自己。
- 以后怎么防:实现完成时顺手删掉该函数里的 `TODO(...)`。
全仓还剩多少没做,一条 `rg "TODO\(" src/` 就能数出来。
- 首次:2026-10-01|复发:0
### 3. 签名里的类型注解和 docstring 说的不一致
- 现象:`def get_rng(random_state: int | None = None)`,docstring 却写"已经是 `np.random.Generator` 时原样返回"。
- 为什么会错:类型注解是给**读代码的人和 pyright** 看的契约。注解说只收 int/None、docstring 说还收 Generator,
两处打架时合法用法会被 IDE 标红,真正的错误反而看不出来。
- 以后怎么防:改签名和改 docstring 必须同一次做完。
本项目所有 `random_state` 的注解统一写 `int | np.integer | np.random.Generator | None`。
- 首次:2026-10-01|复发:1
### 4. 只覆盖自己的状态,忘了第三方库还在用 numpy 旧全局
- 现象:`set_seed` 换掉了本包的生成器,但 sklearn / statsmodels 里 `random_state=None` 的路径
仍不可复现(测试直接挂)。
- 为什么会错:numpy 有**两套**随机状态——旧的 `np.random.*` 全局单例,和新的 `Generator`。
第三方库大量仍在走旧的那套(`check_random_state(None)`),自己新建一个 Generator 管不到它们。
- 以后怎么防:凡是"设全局状态"的函数,先列清单——自己的、numpy 旧的、标准库 `random` 的、
第三方框架的,逐个决定要不要一起设,并在 docstring 里写清到底覆盖了哪几个。
- 首次:2026-10-01|复发:0