1536 字
8 分钟

Python 类型注解完全指南 2026:mypy 静态检查 + Pydantic 数据验证 + 泛型实战

Python 是动态类型语言,但从 3.5 开始引入类型注解,配合 mypy / pyright 可以在运行前发现大量类型错误。良好的类型注解不只是给 IDE 看的——它是代码最好的文档,也是重构时最可靠的安全网。

本文覆盖 2026 年 Python 类型注解的完整最佳实践。


快速决策:选哪种类型工具?#

需求推荐工具
运行时 API 数据验证Pydantic v2
静态类型检查(CI 门禁)mypy(严格模式)
IDE 类型推断pyright(VS Code 内置 Pylance)
配置文件管理(.env)pydantic-settings
复杂泛型 / 接口约束Protocol + TypeVar

一、基础语法(Python 3.10+ 推荐写法)#

# ── 变量注解 ──────────────────────────────────────────────────
name: str = "Alice"
age: int = 30
# 3.10+ 新语法:| 代替 Union,更简洁
def greet(name: str | None = None) -> str:
return f"Hello, {name or 'World'}"
# 3.9+ 内置容器直接用小写(无需 from typing import List)
def process(items: list[int], config: dict[str, str]) -> tuple[int, str]:
return items[0], config.get("key", "")
# 3.12+ type 别名语法
type Vector = list[float]
type Matrix = list[list[float]]
# ── 函数注解 ──────────────────────────────────────────────────
from collections.abc import Callable, Sequence, Iterator
def apply(func: Callable[[int], str], value: int) -> str:
return func(value)
def first(items: Sequence[int]) -> int | None:
return items[0] if items else None
# *args 和 **kwargs
def log(*messages: str, level: str = "INFO") -> None:
pass
def create(**kwargs: str | int) -> dict[str, str | int]:
return kwargs

二、TypeVar 与 Generic(泛型)#

2.1 泛型函数#

from typing import TypeVar
T = TypeVar("T")
def first_item(items: list[T]) -> T | None:
return items[0] if items else None
# mypy 正确推断返回类型
n = first_item([1, 2, 3]) # int | None
s = first_item(["a", "b"]) # str | None
# 有上界约束
from numbers import Number
NumT = TypeVar("NumT", bound=Number)
def double(x: NumT) -> NumT:
return x * 2 # type: ignore[return-value]

2.2 泛型类#

from typing import Generic
K = TypeVar("K")
V = TypeVar("V")
class Stack(Generic[T]):
def __init__(self) -> None:
self._items: list[T] = []
def push(self, item: T) -> None:
self._items.append(item)
def pop(self) -> T:
if not self._items:
raise IndexError("Stack is empty")
return self._items.pop()
def __len__(self) -> int:
return len(self._items)
# 使用时 mypy 能完整推断类型
int_stack: Stack[int] = Stack()
int_stack.push(1)
value = int_stack.pop() # mypy 推断为 int
# 双类型参数
class Pair(Generic[K, V]):
def __init__(self, key: K, value: V) -> None:
self.key = key
self.value = value
def swap(self) -> "Pair[V, K]":
return Pair(self.value, self.key)

三、Protocol(结构子类型)#

from typing import Protocol, runtime_checkable
# 定义协议接口(不需要显式继承,鸭子类型)
@runtime_checkable
class Drawable(Protocol):
def draw(self) -> None: ...
def resize(self, factor: float) -> None: ...
class Circle:
def draw(self) -> None:
print("Drawing circle")
def resize(self, factor: float) -> None:
self.radius *= factor
class Square:
def draw(self) -> None:
print("Drawing square")
def resize(self, factor: float) -> None:
self.side *= factor
# Circle / Square 无需继承 Drawable,mypy 自动识别
def render_all(shapes: list[Drawable]) -> None:
for shape in shapes:
shape.draw()
render_all([Circle(), Square()]) # mypy ✅
print(isinstance(Circle(), Drawable)) # True(@runtime_checkable)

Protocol vs ABC:ABC 需要显式继承(名义子类型);Protocol 只要结构匹配即可(结构子类型)。第三方库的类无法修改时,用 Protocol 约束更灵活。


四、TypedDict / Literal / TypeGuard#

from typing import TypedDict, NotRequired, Literal, Final, TypeGuard
# ── TypedDict ─────────────────────────────────────────────────
class UserDict(TypedDict):
id: int
name: str
email: str
age: NotRequired[int] # 可选字段
user: UserDict = {"id": 1, "name": "Alice", "email": "a@b.com"} # ✅
# user = {"id": "1", ...} # ❌ mypy
# ── Literal:限制值域 ─────────────────────────────────────────
LogLevel = Literal["DEBUG", "INFO", "WARNING", "ERROR"]
def log(message: str, level: LogLevel = "INFO") -> None:
pass
log("msg", "INFO") # ✅
# log("msg", "VERBOSE") # ❌ mypy 报错
# ── Final:常量 ───────────────────────────────────────────────
MAX_RETRIES: Final = 3
API_BASE: Final[str] = "https://api.example.com"
# ── TypeGuard:自定义类型收窄 ─────────────────────────────────
def is_string_list(val: list[object]) -> TypeGuard[list[str]]:
return all(isinstance(item, str) for item in val)
def process(items: list[str] | list[int]) -> None:
if is_string_list(items):
joined = ", ".join(items) # mypy 收窄为 list[str],✅

五、Self 类型(Python 3.11+)#

from typing import Self
class Builder:
def __init__(self) -> None:
self._config: dict[str, str] = {}
def set(self, key: str, value: str) -> Self: # Self 指向实际类型
self._config[key] = value
return self
def build(self) -> dict[str, str]:
return self._config
class AdvancedBuilder(Builder):
def validate(self) -> Self:
return self
# 子类继承时,Self 自动指向 AdvancedBuilder,链式调用类型正确
result = AdvancedBuilder().set("k", "v").validate().build() # ✅

六、mypy 配置#

pyproject.toml
[tool.mypy]
python_version = "3.12"
strict = true # 开启所有严格检查(新项目推荐)
plugins = ["pydantic.mypy"] # Pydantic 插件
# strict 包含:warn_return_any / disallow_untyped_defs
# / disallow_any_generics / check_untyped_defs 等
[[tool.mypy.overrides]]
module = ["some_untyped_lib.*"]
ignore_missing_imports = true # 无类型存根的第三方库
Terminal window
uv run mypy src/ # 检查整个项目
uv run mypy src/main.py # 检查单个文件
# 跳过单行(尽量少用)
result = func() # type: ignore[attr-defined]

七、Pydantic v2 实战#

from pydantic import BaseModel, Field, field_validator, model_validator
from pydantic import ConfigDict
class User(BaseModel):
model_config = ConfigDict(strict=True, frozen=True)
id: int
name: str = Field(min_length=1, max_length=100)
email: str
age: int | None = Field(default=None, ge=0, le=150)
tags: list[str] = Field(default_factory=list)
# 创建 & 序列化
user = User(id=1, name="Alice", email="alice@example.com")
user.model_dump() # → dict(v2 写法,v1 是 .dict())
user.model_dump_json() # → JSON 字符串
User.model_validate({"id": 1, "name": "Alice", "email": "a@b.com"})
# ── 字段验证器 ────────────────────────────────────────────────
class Article(BaseModel):
title: str
tags: list[str]
@field_validator("title")
@classmethod
def strip_title(cls, v: str) -> str:
if not v.strip():
raise ValueError("title cannot be empty")
return v.strip()
@field_validator("tags", mode="before") # 预处理阶段
@classmethod
def parse_tags(cls, v: str | list[str]) -> list[str]:
if isinstance(v, str):
return [t.strip() for t in v.split(",") if t.strip()]
return v
# ── 模型级验证器 ──────────────────────────────────────────────
class DateRange(BaseModel):
start: str
end: str
@model_validator(mode="after")
def check_order(self) -> "DateRange":
if self.start >= self.end:
raise ValueError("end must be after start")
return self
# ── 嵌套模型 ──────────────────────────────────────────────────
class Address(BaseModel):
city: str
country: str = "CN"
class Company(BaseModel):
name: str
address: Address # 嵌套模型自动转换
employees: list[User]
company = Company(
name="Example Corp",
address={"city": "Beijing"}, # dict 自动转 Address ✅
employees=[{"id": 1, "name": "Alice", "email": "a@b.com"}],
)

7.1 pydantic-settings 配置管理#

from pydantic_settings import BaseSettings, SettingsConfigDict
class Settings(BaseSettings):
model_config = SettingsConfigDict(env_file=".env", case_sensitive=False)
app_name: str = "MyApp"
debug: bool = False
database_url: str # 必填(无默认值)
redis_url: str = "redis://localhost:6379"
secret_key: str
from functools import lru_cache
@lru_cache # 单例,避免重复读取 .env
def get_settings() -> Settings:
return Settings()

7.2 FastAPI 完整集成#

from fastapi import FastAPI, Depends
from pydantic import BaseModel, ConfigDict
app = FastAPI()
class CreateUserRequest(BaseModel):
name: str
email: str
age: int | None = None
class UserResponse(BaseModel):
model_config = ConfigDict(from_attributes=True) # ORM 兼容
id: int
name: str
email: str
@app.post("/users", response_model=UserResponse, status_code=201)
async def create_user(request: CreateUserRequest) -> UserResponse:
user = await db_create_user(request.model_dump())
return UserResponse.model_validate(user) # ORM → Pydantic

八、mypy 常见错误速查#

错误信息解决方法
Item "None" of "X | None" has no attribute "y"if x is not None: 判断后再访问
Argument 1 has incompatible type "str"; expected "int"检查调用处实际传入类型
Function is missing a return type annotation添加 -> ReturnType
Missing type parameters for generic type "list"改为 list[int] 等具体类型
Module "xxx" has no attribute "yyy"安装 types-xxxignore_missing_imports

相关文章

本文基于 Python 3.12 / mypy 1.10 / Pydantic v2.8 验证。type 别名新语法需要 Python 3.12+;Self 需要 Python 3.11+。

Python 类型注解完全指南 2026:mypy 静态检查 + Pydantic 数据验证 + 泛型实战
https://971918.xyz/posts/python-guide/python-type-hints-guide/
作者
九所长
发布于
2026-08-03
许可协议
CC BY-NC-SA 4.0