Mypy

← 返回目录 > ← 返回总览

相关工具推荐

工具 说明
Ruff 高性能 Python linter,可替代 Flake8 并集成部分 mypy 功能
Pylint Python 代码质量检查工具,侧重代码风格与逻辑缺陷
Bandit Python 安全漏洞扫描工具
Pre-commit 管理 Git pre-commit 钩子的通用框架

1. 简介

Mypy 是 Python 生态中最主流的可选静态类型检查器(Optional Static Type Checker),由 Jukka Lehtosalo 创建,并得到 Python 之父 Guido van Rossum 的支持。Mypy 遵循 PEP 484 类型注解标准,能够在不运行代码的情况下,通过静态分析检测类型错误,帮助开发者在开发阶段捕获潜在的类型相关 Bug。

  • 主要检查语言:Python
  • 主要检查能力:Python 静态类型检查,在不运行代码的情况下检测类型错误;涵盖静态类型检查
  • 核心检查原理:基于 PEP 484 类型注解标准,支持渐进式类型系统
  • 检查规则/选项:约 50+ 个错误码(分为"默认启用"与"可选启用"两大类),默认启用错误码列表
  • GitHub 仓库https://github.com/python/mypy(20,474 stars)
  • 开源协议:MIT
  • 最新稳定版本:v1.15.0
  • 运行环境要求:Python 3.9+(运行 mypy);可检查 Python 3.9+ 代码
  • 误报率:低

2. 官方文档

文档名称 链接
官方文档首页 https://mypy.readthedocs.io/en/stable/
快速入门指南 https://mypy.readthedocs.io/en/stable/getting_started.html
命令行参数参考 https://mypy.readthedocs.io/en/stable/command_line.html
配置文件详解 https://mypy.readthedocs.io/en/stable/config_file.html
错误码总览 https://mypy.readthedocs.io/en/stable/error_codes.html
默认启用的错误码列表 https://mypy.readthedocs.io/en/stable/error_code_list.html
可选检查的错误码列表 https://mypy.readthedocs.io/en/stable/error_code_list2.html
行内配置说明 https://mypy.readthedocs.io/en/stable/inline_config.html
mypy 守护进程(dmypy) https://mypy.readthedocs.io/en/stable/mypy_daemon.html
与现有代码库集成 https://mypy.readthedocs.io/en/stable/existing_code.html
常见问题排查 https://mypy.readthedocs.io/en/stable/common_issues.html
泛型 https://mypy.readthedocs.io/en/stable/generics.html
协议与结构子类型 https://mypy.readthedocs.io/en/stable/protocols.html
存根文件(Stub files) https://mypy.readthedocs.io/en/stable/stub_files.html
远程缓存 https://mypy.readthedocs.io/en/stable/additional_features.html#remote-cache
PyPI 项目页 https://pypi.org/project/mypy/

3. 社区优秀实践

3.1 Flask(Pallets 团队)

Flask 是 Python 生态中最流行的轻量级 Web 框架之一,由 Pallets 团队维护。Flask 在 pyproject.toml 中配置了严格的 mypy 检查,并通过 tox 运行 mypypyright 双重类型检查。

  • 仓库地址pallets/flask
  • 配置文件pyproject.toml 中的 [tool.mypy]
# 来自 pallets/flask 的 mypy 配置(简化)
[tool.mypy]
python_version = "3.10"
files = ["src", "tests/type_check"]
show_error_codes = true
pretty = true
strict = true

[[tool.mypy.overrides]]
module = [
    "asgiref.*",
    "dotenv.*",
    "cryptography.*",
    "importlib_metadata",
]
ignore_missing_imports = true

实践要点

  • 启用 strict = true 严格模式,对所有源码进行最高级别类型检查
  • 通过 [[tool.mypy.overrides]] 对缺少类型存根的第三方库单独配置 ignore_missing_imports
  • 在 tox 的 typing 环境中同时运行 mypypyright,实现双重保障

3.2 pandas(pandas-dev 团队)

pandas 是 Python 数据科学领域最核心的库之一,代码库规模庞大。pandas 在 pyproject.toml 中配置了详细的 mypy 规则,体现了大型项目渐进式引入严格类型检查的策略。

  • 仓库地址pandas-dev/pandas
  • 配置文件pyproject.toml 中的 [tool.mypy]
# 来自 pandas-dev/pandas 的 mypy 配置(简化)
[tool.mypy]
mypy_path = "typings"
files = ["pandas", "typings"]
ignore_missing_imports = true
python_version = "3.11"
platform = "linux-64"

# 严格类型检查
disallow_untyped_calls = true
disallow_untyped_defs = true
disallow_incomplete_defs = true
check_untyped_defs = true
disallow_untyped_decorators = true

# None 和 Optional 处理
no_implicit_optional = true
strict_optional = true

# 警告配置
warn_redundant_casts = true
warn_unused_ignores = true
warn_no_return = true
enable_error_code = "ignore-without-code"

实践要点

  • 逐项启用严格选项而非直接使用 strict = true,便于团队逐步迁移
  • 使用 mypy_path 指定自定义类型存根目录
  • 启用 enable_error_code = "ignore-without-code",要求所有 # type: ignore 注释必须附带错误码

3.3 Zulip

Zulip 是一个大型开源团队协作平台,Python 代码占比超过 57%,是 mypy 早期采用者之一。Zulip 在 CI 中集成 mypy 检查,类型错误会阻断合并请求。

  • 仓库地址zulip/zulip
  • 生态参考项目:Zulip 是大型 Python 项目引入 mypy 的标杆案例

4. 工具配置说明

4.1 配置文件说明

Mypy 支持多种配置文件格式,按优先级从高到低排列。mypy 不会合并多个配置文件,只会使用找到的第一个配置文件。

配置文件 格式 用途
--config-file 命令行参数 任意 显式指定,最高优先级
mypy.ini INI 项目根目录,传统方式
.mypy.ini INI 隐藏文件形式
pyproject.toml[tool.mypy] 节) TOML 现代 Python 项目推荐
setup.cfg[mypy] 节) INI 与 setuptools 集成
~/.config/mypy/config INI 用户全局配置

4.2 配置文件详解

mypy 配置文件控制类型检查严格度、错误码启用、模块覆盖与排除规则。pyproject.toml[tool.mypy] 节为现代推荐形式。

配置项说明

配置项 类型 说明
python_version string 目标 Python 版本,如 "3.10"
strict bool 启用严格模式(等价于同时启用 disallow_untyped_defsdisallow_any_unimportedno_implicit_optionalwarn_return_anywarn_unused_ignoreswarn_redundant_castsdisallow_subclassing_any 等子选项)
show_error_codes bool 显示错误码,便于精确抑制
warn_return_any bool 警告返回 Any 类型的函数
warn_unused_configs bool 警告未使用的配置节
warn_unused_ignores bool 警告无效果的 # type: ignore 注释
warn_redundant_casts bool 警告冗余的类型转换
pretty bool 美化错误输出
check_untyped_defs bool 检查无类型注解的函数体
disallow_untyped_defs bool 禁止无类型注解的函数定义
disallow_any_generics bool 禁止使用泛型 Any(如 list[Any]
disallow_any_unimported bool 禁止从无类型模块导入 Any
disallow_untyped_calls bool 禁止调用无类型注解的函数
disallow_untyped_decorators bool 禁止无类型注解的装饰器
disallow_subclassing_any bool 禁止继承 Any 类型的类
no_implicit_optional bool 不允许隐式 Optional(默认参数值为 None 时必须显式声明)
exclude string[] 排除的目录或文件 glob 列表
enable_error_code string[] 启用的可选错误码列表,如 truthy-boolignore-without-coderedundant-exprunused-awaitablepossibly-undefined
[[tool.mypy.overrides]] list 按模块覆盖规则,每项含 module(模块名 glob)和该模块下的配置项

推荐配置示例(新项目,严格模式)

适用于新项目或已完成类型注解迁移的项目,提供最高级别的类型安全保障。

# pyproject.toml
[tool.mypy]
# === 基础配置 ===
python_version = "3.10"              # 目标 Python 版本
strict = true                         # 启用严格模式(包含以下所有子选项)
show_error_codes = true               # 显示错误码,便于精确抑制
warn_return_any = true                 # 警告返回 Any 类型的函数
warn_unused_configs = true             # 警告未使用的配置节
warn_unused_ignores = true             # 警告无效果的 # type: ignore 注释
warn_redundant_casts = true            # 警告冗余的类型转换
pretty = true                          # 美化错误输出

# === 排除目录 ===
exclude = [
    "tests/",
    "migrations/",
    ".venv/",
    "build/",
    "dist/",
]

# === 启用可选错误码 ===
enable_error_code = [
    "truthy-bool",                     # 警告隐式布尔转换(如 if x: 其中 x 非 bool)
    "ignore-without-code",              # 要求 # type: ignore 必须指定错误码
    "redundant-expr",                   # 警告冗余表达式
    "unused-awaitable",                 # 警告未 await 的协程
]

strict = true 等价于同时启用以下选项

选项 说明
disallow_untyped_defs 禁止无类型注解的函数定义
disallow_any_unimported 禁止从无类型注解的模块导入 Any 类型
no_implicit_optional 不允许隐式 Optional(默认参数值为 None 时必须显式声明)
warn_return_any 警告返回 Any 类型的函数
warn_unused_ignores 警告无效果的 type: ignore 注释
warn_redundant_casts 警告冗余的类型转换
disallow_subclassing_any 禁止继承 Any 类型的类

推荐配置示例(现有项目渐进式迁移)

适用于正在逐步引入类型注解的现有项目,在保证类型安全的同时降低迁移成本。

# pyproject.toml
[tool.mypy]
python_version = "3.10"
show_error_codes = true
warn_return_any = true
warn_unused_configs = true
warn_unused_ignores = true

# 渐进式:检查无类型注解的函数(但不强制要求有注解)
check_untyped_defs = true

# 渐进式:禁止新增无类型注解的函数
disallow_untyped_defs = true

# 排除尚未迁移的旧代码
exclude = [
    "legacy/",
    "migrations/",
    ".venv/",
]

# 旧模块暂时忽略缺失导入
[[tool.mypy.overrides]]
module = "legacy.*"
ignore_missing_imports = true
check_untyped_defs = false

# 测试文件放宽限制
[[tool.mypy.overrides]]
module = "tests.*"
disallow_untyped_defs = false

推荐配置示例(库/框架开发)

适用于开发供他人使用的 Python 库,要求最严格的类型安全。

# pyproject.toml
[tool.mypy]
python_version = "3.10"
strict = true
show_error_codes = true
warn_return_any = true
warn_unused_configs = true
warn_unused_ignores = true
warn_redundant_casts = true
pretty = true

# 库开发额外选项
disallow_any_generics = true            # 禁止使用泛型 Any(如 list[Any])
disallow_any_unimported = true          # 禁止从无类型模块导入 Any
disallow_untyped_calls = true           # 禁止调用无类型注解的函数
disallow_untyped_decorators = true      # 禁止无类型注解的装饰器

# 启用更多可选检查
enable_error_code = [
    "truthy-bool",
    "ignore-without-code",
    "redundant-expr",
    "unused-awaitable",
    "possibly-undefined",
]

5. 主流集成方式

5.1 pre-commit 集成

pre-commit 在 Python 社区中广泛用于本地提交门禁,可自动运行 mypy 检查暂存区文件。

适用前提:pre-commit 会为 mypy 创建隔离虚拟环境,因此需要通过 additional_dependencies 安装项目所需的类型存根包(如 types-requests)。mypy 默认以 --ignore-missing-imports 运行。

# .pre-commit-config.yaml
repos:
  - repo: https://github.com/pre-commit/mirrors-mypy
    rev: v2.1.0
    hooks:
      - id: mypy
        name: mypy-check
        args: [--strict, --show-error-codes]
        additional_dependencies:
          - types-requests
          - types-PyYAML
          - pydantic
        # 限制只检查 src 目录下的 Python 文件
        files: ^src/
        # 排除迁移目录
        exclude: ^src/migrations/

安装步骤

# 1. 安装 pre-commit
pip install pre-commit

# 2. 安装 Git 钩子
pre-commit install

# 3. 手动对所有文件运行一次检查
pre-commit run --all-files

增量检查:pre-commit 默认仅将暂存区中变更文件传给 hook,天然实现文件级增量。但由于 Mypy 的类型检查依赖 import chain,仅检查单个变更文件可能导致误报(依赖模块的类型信息缺失)。建议在 pre-commit 中使用 pass_filenames: false 并让 Mypy 检查整个项目,同时依赖 Mypy 自身的模块级增量缓存(.mypy_cache)来加速。

# .pre-commit-config.yaml(增量推荐配置)
repos:
  - repo: https://github.com/pre-commit/mirrors-mypy
    rev: v2.1.0
    hooks:
      - id: mypy
        args: [--strict, --show-error-codes]
        additional_dependencies:
          - types-requests
          - types-PyYAML
        # pre-commit 默认仅传入暂存区变更文件
        files: ^src/.*\.py$

5.2 IDE 集成

VS Code

VS Code 通过 Microsoft 官方 Python 扩展提供 mypy 集成支持。推荐使用 Pylance(Python 扩展自带的语言服务器)作为主类型检查器,同时通过终端或 pre-commit 运行 mypy 进行补充检查。

配置路径.vscode/settings.json

{
  // Pylance 类型检查模式(推荐)
  "python.typeChecking.mode": "strict",
}

PyCharm

PyCharm 自 2023.3 版本起内置 mypy 支持。

配置路径File > Settings > Languages & Frameworks > Python > Type Checking

  • 勾选 Use mypy for type checking
  • 指定 mypy 可执行路径(通常自动检测虚拟环境中的 mypy)
  • 指定配置文件路径(如 $PROJECT_DIR$/pyproject.toml

Vim/Neovim

使用 ALE(Vim/Neovim)

" .vimrc 或 init.vim
let g:ale_python_mypy_executable = 'mypy'
let g:ale_python_mypy_options = '--strict --show-error-codes'
let g:ale_linters = {'python': ['mypy']}

5.3 命令行使用方式

# 检查单个文件
mypy script.py

# 检查整个目录
mypy src/

# 严格模式检查
mypy --strict src/

# 显示错误码(推荐,便于精确抑制)
mypy --show-error-codes src/

# 指定 Python 版本
mypy --python-version 3.10 src/

# 指定配置文件
mypy --config-file pyproject.toml src/

# 生成 HTML 报告
mypy --html-report ./report src/

# 显示当前生效的配置
mypy --show-config src/

增量检查:可通过 git diff 筛选变更文件实现文件级增量,但 Mypy 也内置了模块级增量缓存机制。

基于 Git 的命令行增量方案:

# 获取相对于 main 分支的变更 Python 文件,仅对这些文件运行 mypy
mypy $(git diff --name-only --diff-filter=ACMR main... -- '*.py')

局限性:此方案仅检查变更文件本身,不检查依赖这些文件的模块,可能导致遗漏。更适合用于快速本地验证,不建议作为 CI 的唯一检查方式。

Mypy 原生增量能力(模块级):虽然 Mypy 不支持文件级增量,但其内置的模块级增量机制和守护进程模式对大型项目仍有显著的性能提升。

模块级增量缓存(默认启用)

# 首次运行(全量检查,较慢)
mypy src/

# 后续运行(增量检查,自动利用 .mypy_cache 加速)
mypy src/

官方文档The mypy command line -- incremental

dmypy 守护进程(推荐用于开发阶段)

# 启动守护进程并检查(首次运行会自动启动)
dmypy run -- src/

# 后续增量检查(极快,状态保存在内存中)
dmypy run -- src/

# 停止守护进程
dmypy stop

官方文档Mypy daemon (mypy server)

CI 脚本调用

GitHub Actions

# .github/workflows/type-check.yml
name: Type Check

on:
  push:
    branches: [main]
  pull_request:

jobs:
  mypy:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        python-version: ["3.10", "3.11", "3.12"]
    steps:
      - uses: actions/checkout@v4

      - name: Set up Python ${{ matrix.python-version }}
        uses: actions/setup-python@v5
        with:
          python-version: ${{ matrix.python-version }}

      - name: Install dependencies
        run: |
          python -m pip install --upgrade pip
          pip install mypy
          pip install types-requests types-PyYAML

      - name: Cache mypy
        uses: actions/cache@v4
        with:
          path: .mypy_cache
          key: mypy-${{ runner.os }}-${{ matrix.python-version }}-${{ hashFiles('**/*.py') }}

      - name: Run mypy
        run: mypy --strict --show-error-codes src/

GitLab CI

# .gitlab-ci.yml
type-check:
  image: python:3.12-slim
  stage: test
  before_script:
    - pip install mypy types-requests
  script:
    - mypy --strict --show-error-codes src/
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"

增量检查:GitHub Actions 中可按 PR 变更文件触发增量检查。

# .github/workflows/type-check.yml(增量)
name: Type Check

on:
  pull_request:

jobs:
  mypy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0 # 获取完整历史以支持 git diff

      - uses: actions/setup-python@v5
        with:
          python-version: "3.12"

      - name: Install dependencies
        run: |
          pip install mypy
          pip install types-requests types-PyYAML

      - name: Cache mypy
        uses: actions/cache@v4
        with:
          path: .mypy_cache
          key: mypy-${{ runner.os }}-${{ hashFiles('**/*.py') }}

      - name: Run mypy on changed files
        run: |
          CHANGED=$(git diff --name-only --diff-filter=ACMR origin/main... -- '*.py')
          if [ -n "$CHANGED" ]; then
            mypy --strict --show-error-codes $CHANGED
          else
            echo "No Python files changed, skipping mypy."
          fi

5.4 pip / uv / poetry 安装(生态主流)

pip/uv/poetry 是 Python 生态中最主流的包管理方式,也是安装 mypy 的首选途径。

# 基础安装
pip install mypy

# 安装含加速缓存的版本(需要 orjson)
pip install "mypy[faster-cache]"

# 作为开发依赖安装(推荐)
pip install -e ".[dev]"

pyproject.toml 中声明依赖

[project.optional-dependencies]
dev = [
    "mypy",
    "types-requests",
]

5.5 pyproject.toml 配置(推荐)

现代 Python 项目推荐使用 pyproject.toml 作为统一配置文件。

# pyproject.toml
[tool.mypy]
python_version = "3.10"
strict = true
show_error_codes = true
warn_return_any = true
warn_unused_configs = true
warn_unused_ignores = true
exclude = [
    "tests/",
    "migrations/",
]

# 按模块定制规则
[[tool.mypy.overrides]]
module = "tests.*"
disallow_untyped_defs = false
ignore_errors = true

6. 告警抑制(屏蔽)方法

Mypy 提供多层次的告警抑制机制,从行级注释到文件级注释再到全局配置,满足不同粒度的需求。

6.1 通过代码屏蔽:行级注释 # type: ignore

在代码行末添加 # type: ignore 注释,可忽略该行的所有类型错误。

import some_untyped_module  # type: ignore
result = some_untyped_module.process()  # 此行不会被检查

官方文档Spurious errors and locally silencing the checker

6.2 通过代码屏蔽:行级精确屏蔽 # type: ignore[error-code]

通过指定错误码,只忽略特定类型的错误,避免意外屏蔽其他有价值的检查。

from foolib import foo  # type: ignore[attr-defined]

x: int = "hello"  # type: ignore[assignment]

常用错误码包括:attr-definedassignmentarg-typereturn-valueunion-attrimport-untypedoverride 等。

官方文档Silencing errors based on error codes

6.3 通过代码屏蔽:文件级注释

忽略整个文件的所有错误

# mypy: ignore-errors

# 此文件中的所有类型错误都会被忽略
def foo(x, y):
    return x + y

忽略整个文件的特定错误码

# mypy: disable-error-code="truthy-bool, ignore-without-code"

官方文档Ignoring a whole file

6.4 通过代码屏蔽:@no_type_check 装饰器

from typing import no_type_check

@no_type_check
class LegacyClass:
    def process(self, data):
        # 此类中的所有代码不进行类型检查
        return data

官方文档Silencing type errors

6.5 通过工具配置文件屏蔽

按模块忽略错误

# pyproject.toml
[[tool.mypy.overrides]]
module = "third_party.*"
ignore_missing_imports = true

[[tool.mypy.overrides]]
module = "legacy_module"
ignore_errors = true
# mypy.ini
[mypy-third_party.*]
ignore_missing_imports = True

[mypy-legacy_module]
ignore_errors = True

官方文档The mypy configuration file -- exclude

全局禁用/启用特定错误码

[tool.mypy]
# 全局禁用特定错误码
disable_error_code = ["import-untyped", "misc"]

# 全局启用特定错误码
enable_error_code = ["truthy-bool", "ignore-without-code", "redundant-expr"]

官方文档Enabling/disabling specific error codes globally

通过配置文件排除文件/目录

[tool.mypy]
exclude = [
    "^one\\.py$",
    "two\\.pyi$",
    "^three\\.",
]

官方文档The mypy configuration file -- exclude

6.6 通过工具命令行参数屏蔽

# 禁用特定错误码
mypy --disable-error-code import-untyped src/

# 启用特定错误码
mypy --enable-error-code truthy-bool src/

# 忽略缺失导入
mypy --ignore-missing-imports src/

# 允许未类型化的定义
mypy --allow-untyped-defs src/

# 排除匹配正则的文件/目录
mypy --exclude '/build/' src/

官方文档The mypy command line

6.7 通过集成调度工具屏蔽

pre-commit 的文件匹配

# .pre-commit-config.yaml
repos:
  - repo: https://github.com/pre-commit/mirrors-mypy
    rev: v2.1.0
    hooks:
      - id: mypy
        # 仅检查 src 目录下的 .py 文件
        files: ^src/.*\.py$
        # 排除迁移目录
        exclude: ^src/migrations/

← 返回目录 | ← 返回总览