# 类型注解写了一半，Python 侧 mypy 官方入门先接哪几步？

**URL:** <https://www.shiqidu.com/t/topic/1985>\
**Category:** 开发调优\
**Tags:** Linux, 编程, 开源软件, 问与答, Python\
**Created:** [2026 年11 月 8 日 11:53 UTC](https://www.shiqidu.com/t/topic/1985 "2026-11-08T11:53:09Z")\
**Posts on this page:** 1\
**Page:** 1

<div class="post-metadata">

作者： ![shiqidu\_editor](https://www.shiqidu.com/user_avatar/www.shiqidu.com/shiqidu_editor/32/528_2.png) [@shiqidu\_editor](https://www.shiqidu.com/u/shiqidu_editor)\
发布日期： [2026 年11 月 8 日 11:53 UTC](https://www.shiqidu.com/t/topic/1985/1 "2026-11-08T11:53:09Z")

</div>

几个模块已经写上 `str`、`list[str]`、`Optional`，本地解释器照跑，传错类型也不会在提交前拦住。想接静态检查，又怕一把 `--strict` 把 CI 刷红。风格和导入排序若已经在看 [Ruff 能替掉哪一层](https://www.shiqidu.com/t/topic/1972)，那条管的是 lint / format。这里只谈类型，按 [mypy Getting started](https://mypy.readthedocs.io/en/stable/getting_started.html) 渐进接上，不把 Ruff FAQ 再讲一遍。

## 先装上，对单个文件跑一次

文档开篇写明：跑 mypy 需要 **Python 3.10 或更高** 。安装和最小调用是：

```bash
python3 -m pip install mypy
mypy program.py

```

它只做静态检查，不会替你执行代码。报错可以先放着，解释器照样能跑。这是官方默认姿态，不是安装坏了，也不是必须清零才能合并。

直接对存量仓库扫一遍，常常几乎零报错。文档把这当成特性：没注解的函数默认当动态类型，mypy 基本不查。注解没铺开之前，「跑了等于没跑」是正常现象，别据此判断工具没用。更常见的原因是：你点名的文件里，绝大多数函数根本还没有注解。

第一次接入，先挑一个刚改过、注解相对完整的小模块跑通，确认命令、Python 版本、虚拟环境都对，再扩到包路径。环境不一致时，先把解释器对齐，否则争论的是安装问题，不是类型问题。

## 注解一加上，检查才真正开始

Getting started 用 `greeting` 举例：函数没有注解时，把 `int` 或 `bytes` 传进去，运行期会炸，mypy 却默不作声。写成 `def greeting(name: str) -> str:` 之后，同样调用就会报 incompatible type。函数体里乱用运算符，也会被标出来。动态与静态可以按函数粒度切换，迁老代码或写原型时特别省事。

更复杂的形状对照 [Type hints cheat sheet](https://mypy.readthedocs.io/en/stable/cheat_sheet_py3.html)：`list[str]`、`dict[str, float]`、固定长度 `tuple[int, str]`、可变长度 `tuple[int, ...]`，以及 `int | str`、`str | None`。只要「能 for 遍历」而不强求 list，用 `collections.abc.Iterable`。只要只读映射，用 `Mapping`，别随手写 `dict`。Python 3.9+ 集合用内置泛型即可。3.8 及更早才需要 `typing.List` 那一套。注解习惯是 `from typing import …`，不要 `import typing as t` 满天飞，也别 `from typing import *`。

第三方库缺 stub 时，mypy 会提示装 `types-…`（例如 `types-requests`、`types-PyYAML`）。这是类型信息缺失，不是业务逻辑错了。装上 stub 再查，噪音会小很多。发行名和 import 名不一致时（stub 包叫 `types-PyYAML`，代码里却是 `import yaml`），以官方提示为准。库本身若声明了 typing 支持，mypy 会直接读它的注解。

函数体一旦进入静态检查，mypy 会尽量做局部推断：空列表要写成 `output: list[float] = []` 这类，否则它不知道元素类型。`isinstance` 分支里，它通常能收窄联合类型。cheat sheet 里的 `reveal_type(...)` 只适合临时探路，提交前记得删掉。推断和现实不符时，再用 `cast` 或带说明的 `# type: ignore`，别养成「整文件忽略」的习惯。前向引用可用 `from __future__ import annotations`，或把类型写成字符串。

## 存量仓库别一上来 --strict

[Using mypy with an existing codebase](https://mypy.readthedocs.io/en/stable/existing_code.html) 写得很直：大仓先挑一小块（文档举例大概几千到几万行量级）让 mypy 干净通过，再扩大，先看到「有一条绿的检查」。`--strict` 会打开一串更严检查（含 `disallow_untyped_defs` 一类），对「注解覆盖还很稀」的现况往往过猛。更稳的是配置文件里按模块收紧，或先开 `strict = True` 再关掉个别暂时做不到的项。

Getting started 也提醒：`--disallow-untyped-defs`、`--check-untyped-defs` 都是后面加严的旋钮，不是第一天必须全开。第三方库 stub 不齐时，`--ignore-missing-imports` 可暂时用，但更建议按模块配置，而不是全局永久关闭。

官方反复提到几步：

- 把 mypy 调用、同一套选项、同一批文件钉进脚本、`tox` 或 CI，版本也钉住，避免每人本机各跑各的。
- 暂时不打算动的包，可用配置段 `ignore_errors = True`。也可以全局先忽略、只对准备好的模块打开，先保证「已接管的模块」有信号。
- 缺 stub / 找不到实现的导入，优先装 stub 包。到处 `# type: ignore` 会变难维护，宁可按模块设 `ignore_missing_imports`。全局一把关掉导入相关错误码，文档认为可能把后面的问题藏起来，尽量少用。
- 被到处 import 的工具模块、模型类，早点补注解，收益比边角脚本大。
- 新代码要求带注解，改旧代码时顺手补——覆盖率会慢慢涨，不必周末通宵。
- 真要加速迭代，文档还点到 mypy daemon。超大仓再考虑远程缓存，入门阶段大多用不上。

目标可以瞄着「某一天整仓也能过 `mypy --strict`」，但那是渐进结果，不是第一天的开关。existing codebase 页把 `--strict` 拆成多项配置，可按模块逐步打开，例如只对已补完的包启用 `disallow_untyped_defs`，防止回潮。

## 接到哪算够用

日常开发：主路径有注解，CI 固定跑同一命令，就比「只写注解不检查」强一截。原型阶段可以先动态，接口稳定后再补类型——Getting started 明确承认这种节奏。和 Ruff 并行时，各管各的：一个盯风格与一批 lint 规则，一个盯类型。别指望用 formatter 代替类型检查。

你那边是先钉 CI 再补注解，还是先给常改的模块补完再开流水线？
