Python 类型检查 Mypy 详解
==================================

Mypy 是 Python 的静态类型检查器,通过在运行前分析代码类型注解
捕获潜在的类型错误,让 Python 代码享受静态类型语言的安全性。

一、安装与基本使用
------------------

# 安装 Mypy
# pip install mypy

# 验证安装版本
mypy --version

# 检查单个文件
mypy app.py

# 检查整个包
mypy src/

# 严格模式检查(推荐新项目使用)
mypy --strict src/

二、Mypy 配置文件
------------------

# 在 pyproject.toml 中配置 Mypy
"""
[tool.mypy]
# Python 版本设定
python_version = "3.11"

# 严格模式等价于启用以下所有选项
strict = true

# 要求所有函数都有类型注解
disallow_untyped_defs = true

# 禁止未类型化的装饰器
disallow_untyped_decorators = true

# 对 None 值的检查更严格
strict_equality = true

# 检查未使用的忽略注释
warn_unused_ignores = true

# 检查返回值类型与注解不匹配
warn_return_any = true

# 禁止隐式 Optional(如 x = None 不标注 Optional)
no_implicit_optional = true

# 允许重载装饰器
allow_redefinition = false

# 需要导入的模块也要类型检查
follow_imports = "normal"

# 排除不需要检查的文件
exclude = [
"build/",
"tests/",
"migrations/",
]
"""

# 也可以使用 mypy.ini 配置文件
"""
[mypy]
strict = True
disallow_untyped_defs = True
"""

三、@overload 装饰器
---------------------

# @overload 用于为同一个函数声明多个类型签名

from typing import overload, Union

# overload 声明输入 int 返回 int
@overload
def double(value: int) -> int: ...

# overload 声明输入 str 返回 str
@overload
def double(value: str) -> str: ...

# 实际的实现函数(不带类型装饰器)
def double(value):
# 根据输入类型返回不同类型
if isinstance(value, int):
return value * 2
elif isinstance(value, str):
return value + value
raise TypeError("不支持的类型")

# mypy 会检查调用是否匹配重载签名
result_int = double(5) # mypy 推断类型为 int
result_str = double("abc") # mypy 推断类型为 str

四、类型存根文件(Type Stubs)
-------------------------------

# 类型存根是 .pyi 文件,为无类型注解的库提供类型信息

# 安装第三方库的类型存根
# pip install types-requests
# pip install types-PyYAML
# pip install types-setuptools

# 自动生成存根文件
# mypy --generate-stubs mymodule

# 使用 stub 包检测缺失的存根
mypy --install-types

# 手动编写存根文件:example.pyi
"""
# example.pyi 文件内容
def process_data(name: str, count: int = 0) -> list[str]: ...
class DataHandler:
def __init__(self, source: str) -> None: ...
def fetch(self, query: str) -> dict[str, any]: ...
"""

五、reveal_type 调试
---------------------

# reveal_type 是 mypy 的特殊函数,用于调试类型推断

from typing import reveal_type

x = "hello world"
# 在 mypy 运行时会输出:Revealed type is "str"
reveal_type(x)

y = [1, 2, 3]
# 在 mypy 运行时会输出:Revealed type is "list[int]"
reveal_type(y)

z = {"name": "张三", "age": 30}
# 在 mypy 运行时会输出:Revealed type is "dict[str, str | int]"
reveal_type(z)

# reveal_type 仅在 mypy 检查时生效,运行时无任何效果
# 相当于注释,不产生运行时开销

六、类型忽略注释
-----------------

# 当 mypy 误报或需要绕过类型检查时使用忽略注释

from typing import Any

# 忽略特定行的类型错误
value: Any = some_external_function() # type: ignore

# 指定忽略的错误码
x = unsafe_function() # type: ignore[assignment]

# 对整个文件级别的忽略
"""
# mypy: ignore-errors
"""

# 在 pyproject.toml 中忽略特定模块的错误
"""
[[tool.mypy.overrides]]
module = "legacy_module.*"
ignore_errors = true
"""

七、高级类型特性
----------------

from typing import TypedDict, Literal, Protocol

# TypedDict —— 字典的结构化类型
class UserDict(TypedDict):
# 定义字典的键值类型
name: str
age: int
email: str

user: UserDict = {"name": "李四", "age": 28, "email": "li@example.com"}

# Literal —— 限定字面量值
def set_mode(mode: Literal["dev", "prod", "test"]) -> None:
# mode 只能是 "dev" "prod" "test" 之一
print(f"模式设置为: {mode}")

# Protocol —— 结构子类型(鸭子类型)
class Drawable(Protocol):
# 定义协议:任何有 draw 方法的类型
def draw(self) -> None: ...

def render(obj: Drawable) -> None:
# 不需要继承,只需有 draw 方法
obj.draw()

八、与项目集成
--------------

# 在 pre-commit 中配置 mypy
"""
repos:
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.7.0
hooks:
- id: mypy
args: [--strict, --ignore-missing-imports]
additional_dependencies: [types-requests]
"""

# 在 CI 中运行 mypy
mypy src/ --strict --warn-unused-configs

# 检查并生成报告
mypy src/ --html-report mypy_report
# 生成 HTML 格式的类型覆盖报告

# Mypy 将 Python 的动态灵活性与静态类型的严谨性结合,
# 在大型项目中显著降低运行时类型错误的发生率。

 

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐