
为什么 Python 类型提示很重要
Python 是动态类型语言,但类型提示(自 Python 3.5 引入,3.12+ 有重大改进)允许你用期望的类型注释代码。它们不影响运行时行为——Python 在执行时仍然忽略它们——但它们支持:
- 静态类型检查,使用 mypy、Pyright 或类似工具
- 更好的 IDE 支持——自动补全、重构和内联文档
- 自文档化 API——函数签名传达意图,无需文档字符串
- 早期发现错误——在代码运行之前

基本注解
# 变量
name: str = "Alice"
age: int = 30
score: float = 9.8
active: bool = True
# 函数参数和返回类型
def greet(name: str) -> str:
return f"Hello, {name}!"
def add(a: int, b: int) -> int:
return a + b
def process(items: list) -> None: # 无返回值
for item in items:
print(item)
内置集合类型(Python 3.9+)
# Python 3.9+ — 直接使用内置类型
def get_ids() -> list[int]:
return [1, 2, 3]
def get_config() -> dict[str, str]:
return {"host": "localhost", "port": "5432"}
def get_scores() -> tuple[int, int, int]:
return (95, 87, 92)
def get_unique_tags() -> set[str]:
return {"python", "typing", "tutorial"}
# Python 3.8 及以下 — 使用 typing 模块
from typing import List, Dict, Tuple, Set
def get_ids() -> List[int]: ...
def get_config() -> Dict[str, str]: ...
Optional 和 Union 类型
from typing import Optional, Union # 或 Python 3.10+ 使用 |
# Optional[T] 等价于 Union[T, None]
def find_user(user_id: int) -> Optional[str]: # str 或 None
if user_id > 0:
return "Alice"
return None
# Python 3.10+ 语法
def find_user(user_id: int) -> str | None: # 等价
...
# Union:可以是多种类型
def process(value: Union[int, str]) -> str:
return str(value)
# Python 3.10+ 联合语法
def process(value: int | str) -> str:
return str(value)
# 常见错误——对可空类型永远不要这样做
def bad(name: str = None) -> None: # 类型检查器会报错
pass
def good(name: str | None = None) -> None: # 正确
pass
TypedDict——类型化字典
from typing import TypedDict
class UserDict(TypedDict):
id: int
name: str
email: str
class PartialUserDict(TypedDict, total=False): # 所有键可选
name: str
email: str
def create_user(data: UserDict) -> UserDict:
return data
# 类型安全:mypy 检查键名和值类型
user: UserDict = {
"id": 1,
"name": "Alice",
"email": "alice@example.com",
}
create_user({"id": 1, "name": "Alice"}) # ❌ mypy 错误:缺少 email
create_user({"id": "1", "name": "Alice", "email": "a@b.com"}) # ❌ id 必须是 int

泛型
from typing import TypeVar, Generic
T = TypeVar('T')
K = TypeVar('K')
V = TypeVar('V')
# 泛型函数
def first(items: list[T]) -> T | None:
return items[0] if items else None
result = first([1, 2, 3]) # 推断类型:int | None
result = first(["a", "b"]) # 推断类型:str | None
# 泛型类
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:
return self._items.pop()
def peek(self) -> T | None:
return self._items[-1] if self._items else None
stack: Stack[int] = Stack()
stack.push(1)
stack.push("hello") # ❌ mypy 错误:期望 int,得到 str
Protocol——结构类型(鸭子类型)
from typing import Protocol
# 定义类型必须能做什么
class Drawable(Protocol):
def draw(self) -> None: ...
class Serializable(Protocol):
def to_json(self) -> str: ...
# 任何具有 draw() 方法的类都满足 Drawable——无需继承
class Circle:
def draw(self) -> None:
print("Drawing circle")
class Square:
def draw(self) -> None:
print("Drawing square")
def render_all(shapes: list[Drawable]) -> None:
for shape in shapes:
shape.draw()
# ✅ 有效,因为 Circle 和 Square 都有 draw()
render_all([Circle(), Square()])
# 对于无法修改的第三方类型很有用
class FileWriter(Protocol):
def write(self, data: bytes) -> int: ...
def close(self) -> None: ...
def save_data(writer: FileWriter, data: bytes) -> None:
writer.write(data)
writer.close()
Callable 类型
from typing import Callable
# 一个接受 int 并返回 str 的函数
def apply(func: Callable[[int], str], value: int) -> str:
return func(value)
# 可变参数
def run(callback: Callable[..., None]) -> None:
callback()
# 返回可调用对象(高阶函数)
def make_multiplier(factor: int) -> Callable[[int], int]:
def multiply(x: int) -> int:
return x * factor
return multiply
Literal 类型
from typing import Literal
Direction = Literal['north', 'south', 'east', 'west']
HttpMethod = Literal['GET', 'POST', 'PUT', 'DELETE', 'PATCH']
def move(direction: Direction) -> None:
print(f"Moving {direction}")
move('north') # ✅
move('diagonal') # ❌ mypy 错误:不是有效的 Direction
def make_request(method: HttpMethod, url: str) -> None: ...

dataclasses 和 attrs 与类型
from dataclasses import dataclass, field
@dataclass
class Product:
id: int
name: str
price: float
tags: list[str] = field(default_factory=list)
active: bool = True
def discount_price(self, pct: float) -> float:
return self.price * (1 - pct)
p = Product(id=1, name="Widget", price=9.99)
p.discount_price(0.1) # 8.991
类型收窄
def process(value: int | str) -> str:
if isinstance(value, int):
# mypy 现在知道在此分支中 value: int
return str(value * 2)
else:
# mypy 现在知道在此分支中 value: str
return value.upper()
# assert 收窄类型
def get_name(user: dict | None) -> str:
assert user is not None, "user must not be None"
# mypy 知道此处 user: dict
return user.get("name", "")
# TypeGuard 用于自定义类型收窄函数
from typing import TypeGuard
def is_string_list(lst: list[object]) -> TypeGuard[list[str]]:
return all(isinstance(x, str) for x in lst)
def join_strings(items: list[object]) -> str:
if is_string_list(items):
return ", ".join(items) # mypy 知道 items: list[str]
return ""
运行 mypy
# 安装
pip install mypy
# 检查文件
mypy script.py
# 使用严格模式(推荐用于新项目)
mypy --strict script.py
# 常用标志
mypy --ignore-missing-imports # 跳过无类型注解的第三方库
mypy --check-untyped-defs # 类型检查无注解的函数
# pyproject.toml 配置
[tool.mypy]
strict = true
ignore_missing_imports = true
常见问题
问:类型提示会减慢 Python 吗?
不会。Python 在运行时忽略注解(它们作为元数据存储,默认不评估)。使用 from __future__ import annotations 导入使它们成为字符串,开销更小。
问:我应该注解每个变量吗?
只在类型从上下文不明显时注解。函数签名应始终注解。变量赋值中类型从值明确(例如 x = 5)不需要注解。
问:如何处理没有类型存根的第三方库?
在特定行使用 # type: ignore,使用 py.typed 存根,或安装存根包(pip install types-requests)。查看 typeshed 获取社区维护的存根。
→ 使用 JSON Viewer 探索和验证 Python 数据结构。