Skip to content

类型检查工具 ​

类型注解本身不影响 Python 运行时行为。类型检查工具(mypy、pyright)在运行前发现类型错误。

mypy:标准类型检查器 ​

安装和基本使用 ​

bash
pip install mypy

# 检查单个文件
mypy script.py

# 检查整个目录
mypy src/

# 显示更多信息
mypy --show-error-codes script.py

示例 ​

代码(example.py):

python
def add(a: int, b: int) -> int:
    return a + b

result = add(5, 10)
print(result)

# 类型错误
result = add("5", "10")

运行 mypy:

bash
$ mypy example.py
example.py:7: error: Argument 1 to "add" has incompatible type "str"; expected "int"
example.py:7: error: Argument 2 to "add" has incompatible type "str"; expected "int"
Found 2 errors in 1 file (checked 1 source file)

配置文件 ​

创建 mypy.ini 或 pyproject.toml:

mypy.ini:

ini
[mypy]
python_version = 3.12
warn_return_any = True
warn_unused_configs = True
disallow_untyped_defs = True

[mypy-tests.*]
disallow_untyped_defs = False

pyproject.toml:

toml
[tool.mypy]
python_version = "3.12"
warn_return_any = true
warn_unused_configs = true
disallow_untyped_defs = true

[[tool.mypy.overrides]]
module = "tests.*"
disallow_untyped_defs = false

常用配置 ​

选项说明
disallow_untyped_defs禁止无类型注解的函数
disallow_any_generics禁止 list 而非 list[int]
warn_redundant_casts警告多余的类型转换
warn_unused_ignores警告多余的 # type: ignore
strict启用所有严格检查

pyright/pylance ​

微软开发的高性能类型检查器,集成在 VS Code 的 Pylance 扩展中。

安装 ​

bash
npm install -g pyright

# 或使用 pipx
pipx install pyright

使用 ​

bash
pyright script.py
pyright src/

配置 ​

创建 pyrightconfig.json:

json
{
  "pythonVersion": "3.12",
  "typeCheckingMode": "strict",
  "reportMissingTypeStubs": false,
  "exclude": [
    "**/node_modules",
    "**/__pycache__"
  ]
}

渐进类型(Gradual Typing) ​

无需一次性为所有代码添加类型:

阶段 1:关键接口 ​

python
# 只为公共 API 添加类型
def process_data(data: list[dict]) -> dict:
    # 内部实现无类型
    result = {}
    for item in data:
        key = item["id"]
        result[key] = item
    return result

阶段 2:关键路径 ​

python
def process_data(data: list[dict[str, any]]) -> dict[int, dict]:
    result: dict[int, dict] = {}
    for item in data:
        key: int = item["id"]
        result[key] = item
    return result

阶段 3:完整类型 ​

python
from typing import TypedDict

class Item(TypedDict):
    id: int
    name: str

def process_data(data: list[Item]) -> dict[int, Item]:
    result: dict[int, Item] = {}
    for item in data:
        result[item["id"]] = item
    return result

Any:动态类型逃生舱 ​

Any 关闭类型检查,用于处理真正动态的数据:

python
from typing import Any
import json

def load_config(path: str) -> Any:
    with open(path) as f:
        return json.load(f)  # JSON 可以是任何结构

config = load_config("config.json")
config.database.host  # 类型检查器不报错

限制 Any 的传播:

python
from typing import Any, cast

def load_config(path: str) -> dict[str, Any]:
    with open(path) as f:
        data = json.load(f)
        return cast(dict[str, Any], data)  # 明确类型

类型忽略 ​

单行忽略 ​

python
result = some_function()  # type: ignore

# 指定错误码
result = some_function()  # type: ignore[arg-type]

文件级忽略 ​

python
# mypy: ignore-errors

# 整个文件不检查
def untyped_function(x, y):
    return x + y

何时使用 ​

  • 与无类型第三方库交互
  • 已知的类型系统限制
  • 临时绕过(应添加 TODO 注释)

不要滥用:过度使用会失去类型检查的好处。

第三方库类型存根 ​

typeshed ​

Python 标准库和常用第三方库的类型存根:

bash
# 大多数库已包含类型信息
pip install requests  # 自带类型

# 部分老库需要单独安装类型存根
pip install types-requests

为无类型库创建存根 ​

项目结构:

my_project/
├── src/
│   └── main.py
└── stubs/
    └── untyped_lib/
        └── __init__.pyi

存根文件(__init__.pyi):

python
def some_function(x: int, y: str) -> bool: ...

class SomeClass:
    def method(self) -> None: ...

mypy 配置:

ini
[mypy]
mypy_path = stubs

常见类型错误 ​

错误 1:incompatible type ​

python
def add(a: int, b: int) -> int:
    return a + b

add("5", "10")  # error: Argument 1 has incompatible type "str"; expected "int"

修复:传入正确类型或修改签名。

错误 2:missing return ​

python
def divide(a: int, b: int) -> int:
    if b != 0:
        return a // b
    # error: Missing return statement

修复:处理所有路径。

错误 3:untyped function ​

python
# error: Function is missing a type annotation
def process(data):
    return data * 2

修复:添加类型注解或在配置中放宽限制。

实战:为已有项目添加类型 ​

步骤 1:生成存根 ​

bash
stubgen -p mypackage -o stubs/

步骤 2:启用 mypy ​

bash
mypy --install-types
mypy src/ --ignore-missing-imports

步骤 3:逐步修复 ​

从核心模块开始,逐个文件添加类型:

python
# 第1周:为公共 API 添加类型
# 第2周:为核心业务逻辑添加类型
# 第3周:为工具函数添加类型

步骤 4:集成 CI ​

yaml
# .github/workflows/type-check.yml
- name: Type check
  run: mypy src/

小结 ​

  • mypy 是标准类型检查器
  • pyright 性能更高,VS Code 集成
  • 渐进类型 允许逐步添加类型
  • Any 是动态类型逃生舱,应谨慎使用
  • # type: ignore 临时绕过检查
  • typeshed 提供第三方库类型存根

回到 类型系统概览 或查看 练习答案。


参考:

用心记录每一步成长