共计 11642 个字符,预计需要花费 30 分钟才能阅读完成。
本文全面讲解 Python 的类型注解系统,包括基础用法、高级特性、版本演进和最佳实践。
目录
1. 类型注解简介
什么是类型注解?
类型注解(Type Hints)是 Python 3.5 引入的特性,允许在代码中标注变量、函数参数和返回值的类型。
重要: 类型注解是可选的,不影响运行时行为,主要用于:
- 代码文档化
- IDE 智能提示
- 静态类型检查(mypy 等工具)
- 提高代码可维护性
# 没有类型注解
def greet(name):
return f"Hello, {name}"
# 有类型注解
def greet(name: str) -> str:
return f"Hello, {name}"
为什么使用类型注解?
# 优势 1:IDE 智能提示
def calculate_area(radius: float) -> float:
return 3.14 * radius ** 2
# IDE 会提示 radius 应该是 float 类型
result = calculate_area(5.0) # ✓
# result = calculate_area("5") # IDE 会警告
# 优势 2:提前发现错误
def process_items(items: list[str]) -> int:
return len(items)
# 优势 3:代码文档化
def fetch_user(user_id: int) -> dict[str, str]:
"""
通过类型注解就能知道:- user_id 是整数
- 返回字典,键和值都是字符串
"""return {"name":"Alice","email":"alice@example.com"}
2. 基础类型注解
内置类型
# 基本类型
age: int = 25
price: float = 19.99
name: str = "Alice"
is_active: bool = True
data: bytes = b"hello"
# None 类型
result: None = None
# Any 类型(任意类型)from typing import Any
value: Any = "can be anything"
value = 123 # 也可以
变量注解
# Python 3.6+:变量注解
count: int
count = 10
# 可以不立即赋值
name: str
# ... 稍后赋值
name = "Bob"
# 类属性注解
class User:
name: str
age: int
def __init__(self, name: str, age: int):
self.name = name
self.age = age
3. 容器类型注解
List、Dict、Set、Tuple
from typing import List, Dict, Set, Tuple
# Python 3.9 之前
names: List[str] = ["Alice", "Bob"]
scores: Dict[str, int] = {"Alice": 95, "Bob": 87}
tags: Set[int] = {1, 2, 3}
point: Tuple[int, int] = (10, 20)
# Python 3.9+:使用内置类型(推荐)names: list[str] = ["Alice", "Bob"]
scores: dict[str, int] = {"Alice": 95, "Bob": 87}
tags: set[int] = {1, 2, 3}
point: tuple[int, int] = (10, 20)
嵌套容器
# 嵌套列表
matrix: list[list[int]] = [[1, 2], [3, 4]]
# 复杂字典
user_data: dict[str, list[int]] = {"Alice": [95, 87, 92],
"Bob": [78, 85, 90]
}
# 字典的字典
config: dict[str, dict[str, str]] = {"database": {"host": "localhost", "port": "5432"},
"cache": {"host": "localhost", "port": "6379"}
}
可变长度元组
# 固定长度元组
point: tuple[int, int] = (10, 20)
# 可变长度元组
numbers: tuple[int, ...] = (1, 2, 3, 4, 5)
# 空元组
empty: tuple[()] = ()
4. 函数类型注解
基本函数注解
# 参数和返回值注解
def add(a: int, b: int) -> int:
return a + b
# 无返回值
def log_message(message: str) -> None:
print(message)
# 多个参数
def create_user(name: str, age: int, email: str) -> dict[str, str | int]:
return {"name": name, "age": age, "email": email}
默认参数
def greet(name: str, greeting: str = "Hello") -> str:
return f"{greeting}, {name}"
# 可选参数
from typing import Optional
def find_user(user_id: int) -> Optional[dict]:
# 可能返回 dict 或 None
if user_id > 0:
return {"id": user_id, "name": "Alice"}
return None
可变参数
# *args 和 **kwargs
def sum_all(*args: int) -> int:
return sum(args)
def print_info(**kwargs: str) -> None:
for key, value in kwargs.items():
print(f"{key}: {value}")
# 混合使用
def process(
required: str,
*args: int,
optional: str = "default",
**kwargs: str
) -> None:
pass
函数类型
from typing import Callable
# 函数作为参数
def apply_operation(x: int, y: int, op: Callable[[int, int], int]) -> int:
return op(x, y)
def add(a: int, b: int) -> int:
return a + b
result = apply_operation(5, 3, add)
# 无参数函数
def run_callback(callback: Callable[[], None]) -> None:
callback()
# 任意参数函数
from typing import Any
def run_function(func: Callable[..., Any]) -> Any:
return func()
5. 高级类型
Union 类型
from typing import Union
# Union:多种类型之一
def process_id(id: Union[int, str]) -> str:
return str(id)
process_id(123) # ✓
process_id("abc") # ✓
# Python 3.10+:使用 | 运算符(推荐)def process_id(id: int | str) -> str:
return str(id)
Optional 类型
from typing import Optional
# Optional[X] 等价于 Union[X, None]
def find_user(user_id: int) -> Optional[dict]:
if user_id > 0:
return {"id": user_id}
return None
# Python 3.10+
def find_user(user_id: int) -> dict | None:
if user_id > 0:
return {"id": user_id}
return None
Literal 类型
from typing import Literal
# 限制为特定值
def set_mode(mode: Literal["read", "write", "append"]) -> None:
print(f"Mode: {mode}")
set_mode("read") # ✓
# set_mode("delete") # 类型检查错误
# 多个字面量
Status = Literal["pending", "approved", "rejected"]
def update_status(status: Status) -> None:
pass
Final 类型
from typing import Final
# 常量(不应被修改)MAX_SIZE: Final = 100
API_KEY: Final[str] = "secret-key"
# MAX_SIZE = 200 # 类型检查器会警告
# 类中的 Final
class Config:
MAX_RETRIES: Final = 3
def __init__(self):
self.timeout: Final = 30 # 实例常量
TypedDict
from typing import TypedDict
# 定义字典结构
class User(TypedDict):
name: str
age: int
email: str
user: User = {
"name": "Alice",
"age": 25,
"email": "alice@example.com"
}
# 可选字段
class UserOptional(TypedDict, total=False):
name: str
age: int
email: str # 可选
# Python 3.11+:Required 和 NotRequired
from typing import Required, NotRequired
class UserMixed(TypedDict):
name: Required[str] # 必需
age: NotRequired[int] # 可选
NewType
from typing import NewType
# 创建新类型(用于区分相同基础类型)UserId = NewType('UserId', int)
ProductId = NewType('ProductId', int)
def get_user(user_id: UserId) -> dict:
return {"id": user_id}
def get_product(product_id: ProductId) -> dict:
return {"id": product_id}
user_id = UserId(123)
product_id = ProductId(456)
get_user(user_id) # ✓
# get_user(product_id) # 类型检查错误
# get_user(123) # 类型检查错误
6. 泛型
泛型函数
from typing import TypeVar, List
# 定义类型变量
T = TypeVar('T')
def first(items: list[T]) -> T:
return items[0]
# 自动推断类型
numbers = [1, 2, 3]
first_num = first(numbers) # 推断为 int
names = ["Alice", "Bob"]
first_name = first(names) # 推断为 str
新写法 (Python 3.12+)
# 1. 直接写 [T],意思就是:我要定义一个泛型 T
def first[T](items: list[T]) -> T:
return items[0]
泛型类
from typing import Generic, TypeVar
T = TypeVar('T')
class Stack(Generic[T]):
def __init__(self):
self._items: list[T] = []
def push(self, item: T) -> None:
self._items.append(item)
def pop(self) -> T:
return self._items.pop()
# 使用泛型类
int_stack: Stack[int] = Stack()
int_stack.push(1)
int_stack.push(2)
str_stack: Stack[str] = Stack()
str_stack.push("hello")
新写法
class Box[T]:
def __init__(self, item: T):
self.item = item
def get(self) -> T:
return self.item
# 使用
box1 = Box(123) # T 推断为 int
box2 = Box("Hello") # T 推断为 str
约束泛型
from typing import TypeVar
# 约束为特定类型
T = TypeVar('T', int, float)
def add(a: T, b: T) -> T:
return a + b
add(1, 2) # ✓ int
add(1.5, 2.5) # ✓ float
# add("a", "b") # 错误
# 约束为某个基类的子类
from typing import TypeVar
class Animal:
pass
class Dog(Animal):
pass
T = TypeVar('T', bound=Animal)
def process_animal(animal: T) -> T:
return animal
process_animal(Dog()) # ✓
# process_animal("not an animal") # 错误
7. 协议和结构化子类型
Protocol(Python 3.8+)
from typing import Protocol
# 定义协议
class Drawable(Protocol):
def draw(self) -> None:
...
# 任何实现了 draw 方法的类都满足协议
class Circle:
def draw(self) -> None:
print("Drawing circle")
class Square:
def draw(self) -> None:
print("Drawing square")
def render(shape: Drawable) -> None:
shape.draw()
render(Circle()) # ✓
render(Square()) # ✓
运行时可检查的协议
from typing import Protocol, runtime_checkable
@runtime_checkable
class Closeable(Protocol):
def close(self) -> None:
...
class File:
def close(self) -> None:
print("Closing file")
f = File()
print(isinstance(f, Closeable)) # True
8. 类型别名
简单类型别名
# 类型别名
Vector = list[float]
def scale(vector: Vector, factor: float) -> Vector:
return [x * factor for x in vector]
# 复杂类型别名
JSON = dict[str, Any]
Headers = dict[str, str]
Callback = Callable[[int], None]
TypeAlias(Python 3.10+)
from typing import TypeAlias
# 显式声明类型别名
Vector: TypeAlias = list[float]
JSON: TypeAlias = dict[str, Any]
9. 版本演进
Python 3.5(PEP 484)
- 引入 typing 模块
- 基础类型注解
- List, Dict, Set, Tuple 等
from typing import List, Dict
def process(items: List[str]) -> Dict[str, int]:
return {item: len(item) for item in items}
Python 3.6(PEP 526)
- 变量注解语法
# 变量注解
name: str = "Alice"
count: int
class User:
name: str
age: int
Python 3.7
- 延迟注解评估(
from __future__ import annotations)
from __future__ import annotations
class Node:
def __init__(self, value: int, next: Node | None = None):
self.value = value
self.next = next # 可以引用自身
Python 3.8(PEP 544)
- Protocol 协议
- Literal 类型
- Final 类型
- TypedDict
from typing import Protocol, Literal, Final, TypedDict
class Drawable(Protocol):
def draw(self) -> None: ...
Mode = Literal["r", "w", "a"]
MAX_SIZE: Final = 100
class User(TypedDict):
name: str
age: int
Python 3.9(PEP 585)
- 使用内置类型进行泛型注解(不需要 typing 模块)
# Python 3.9+:直接使用内置类型
def process(items: list[str]) -> dict[str, int]:
return {item: len(item) for item in items}
# 不再需要
from typing import List, Dict
Python 3.10(PEP 604, 612, 613)
- Union 类型使用 | 运算符
- TypeAlias
- ParamSpec
# Union 使用 |
def process(value: int | str) -> str:
return str(value)
# TypeAlias
from typing import TypeAlias
Vector: TypeAlias = list[float]
# ParamSpec(高级)from typing import ParamSpec, Callable
P = ParamSpec('P')
def decorator(func: Callable[P, int]) -> Callable[P, int]:
def wrapper(*args: P.args, **kwargs: P.kwargs) -> int:
return func(*args, **kwargs)
return wrapper
Python 3.11
- Self 类型
- TypeVarTuple
- Required 和 NotRequired
from typing import Self
class Builder:
def set_name(self, name: str) -> Self:
self.name = name
return self
# Required 和 NotRequired
from typing import TypedDict, Required, NotRequired
class User(TypedDict):
name: Required[str]
age: NotRequired[int]
Python 3.12
- 泛型语法改进
- type 语句
# 新的泛型语法
def first[T](items: list[T]) -> T:
return items[0]
class Stack[T]:
def __init__(self):
self._items: list[T] = []
# type 语句
type Vector = list[float]
type Matrix = list[Vector]
10. 类型检查工具
mypy
最流行的 Python 静态类型检查器。
# 安装
pip install mypy
# 检查文件
mypy script.py
# 检查目录
mypy src/
配置文件(mypy.ini):
[mypy]
python_version = 3.11
warn_return_any = True
warn_unused_configs = True
disallow_untyped_defs = True
pyright
微软开发的类型检查器,速度快。
# 安装
pip install pyright
# 检查
pyright
pyre
Facebook 开发的类型检查器。
# 安装
pip install pyre-check
# 初始化
pyre init
# 检查
pyre check
11. 最佳实践
1. 渐进式类型注解
# 从关键函数开始
def calculate_total(items: list[dict]) -> float:
return sum(item['price'] for item in items)
# 逐步添加更详细的类型
from typing import TypedDict
class Item(TypedDict):
name: str
price: float
quantity: int
def calculate_total(items: list[Item]) -> float:
return sum(item['price'] * item['quantity'] for item in items)
2. 使用类型别名提高可读性
# 不好
def process(data: dict[str, list[tuple[int, str]]]) -> list[dict[str, int]]:
pass
# 好
UserId = int
UserName = str
UserData = tuple[UserId, UserName]
UserMap = dict[str, list[UserData]]
Result = list[dict[str, int]]
def process(data: UserMap) -> Result:
pass
3. 避免过度使用 Any
from typing import Any
# 不好
def process(data: Any) -> Any:
return data
# 好:尽可能具体
def process(data: dict[str, int]) -> list[int]:
return list(data.values())
# 如果真的不确定,使用泛型
from typing import TypeVar
T = TypeVar('T')
def identity(value: T) -> T:
return value
4. 使用 Protocol 而非 ABC
# 不够灵活
from abc import ABC, abstractmethod
class Drawable(ABC):
@abstractmethod
def draw(self) -> None:
pass
# 更灵活:使用 Protocol
from typing import Protocol
class Drawable(Protocol):
def draw(self) -> None:
...
# 任何有 draw 方法的类都自动满足
class Circle:
def draw(self) -> None:
print("Drawing circle")
5. 使用 TypedDict 而非普通 dict
# 不好
def create_user(name: str, age: int) -> dict:
return {"name": name, "age": age}
# 好
from typing import TypedDict
class User(TypedDict):
name: str
age: int
def create_user(name: str, age: int) -> User:
return {"name": name, "age": age}
6. 处理向后兼容
# Python 3.7-3.8 兼容
from __future__ import annotations
from typing import Optional
def process(value: int | None) -> str: # 使用 | 语法
return str(value) if value else "None"
# 或者使用 typing_extensions
from typing_extensions import Literal
Mode = Literal["read", "write"]
7. 文档字符串与类型注解结合
def calculate_discount(
price: float,
discount_rate: float,
min_price: float = 0.0
) -> float:
"""
计算折扣后的价格。Args:
price: 原价
discount_rate: 折扣率(0- 1 之间)min_price: 最低价格
Returns:
折扣后的价格
Raises:
ValueError: 如果 discount_rate 不在 0 - 1 之间
"""
if not 0 <= discount_rate <= 1:
raise ValueError("折扣率必须在 0 - 1 之间")
discounted = price * (1 - discount_rate)
return max(discounted, min_price)
8. 使用 reveal_type 调试类型
# 使用 mypy 的 reveal_type 查看推断的类型
def process(items: list[int]) -> None:
reveal_type(items) # mypy 会显示:list[int]
first = items[0]
reveal_type(first) # mypy 会显示:int
12. 常见模式和技巧
工厂函数
from typing import TypeVar, Type
T = TypeVar('T')
def create_instance(cls: Type[T], *args, **kwargs) -> T:
return cls(*args, **kwargs)
class User:
def __init__(self, name: str):
self.name = name
user = create_instance(User, "Alice") # 类型推断为 User
装饰器类型注解
from typing import Callable, TypeVar, ParamSpec
P = ParamSpec('P')
R = TypeVar('R')
def log_calls(func: Callable[P, R]) -> Callable[P, R]:
def wrapper(*args: P.args, **kwargs: P.kwargs) -> R:
print(f"Calling {func.__name__}")
return func(*args, **kwargs)
return wrapper
@log_calls
def add(a: int, b: int) -> int:
return a + b
上下文管理器
from typing import ContextManager
from contextlib import contextmanager
@contextmanager
def open_file(filename: str) -> ContextManager[str]:
f = open(filename)
try:
yield f.read()
finally:
f.close()
异步函数
from typing import Coroutine, Any
async def fetch_data(url: str) -> dict[str, Any]:
# 模拟异步操作
return {"data": "example"}
# 返回类型是 Coroutine
def get_fetcher() -> Coroutine[Any, Any, dict[str, Any]]:
return fetch_data("http://example.com")
13. 总结
核心要点
- 类型注解是可选的 :不影响运行时,主要用于文档和静态检查
- 渐进式采用 :从关键函数开始,逐步完善
- 版本差异 :Python 3.9+ 可以直接使用内置类型,3.10+ 支持 | 语法
- 工具支持 :使用 mypy、pyright 等工具进行类型检查
- 最佳实践 :使用类型别名、Protocol、TypedDict 提高代码质量
版本选择建议
- Python 3.9+:使用内置类型(list, dict 等)
- Python 3.10+:使用 | 运算符替代 Union
- Python 3.11+:使用 Self、Required/NotRequired
- Python 3.12+:使用新的泛型语法
学习路径
- 基础 :基本类型注解、函数注解
- 进阶 :容器类型、Optional、Union
- 高级 :泛型、Protocol、TypedDict
- 专家 :ParamSpec、Concatenate、高级泛型
参考资源
- PEP 484 - Type Hints
- PEP 585 - Type Hinting Generics In Standard Collections
- PEP 604 - Allow writing union types as X | Y
- mypy 文档
- typing 模块文档
结语
类型注解是 Python 现代化的重要特性,虽然是可选的,但能显著提高代码质量和可维护性。建议在新项目中积极使用,在旧项目中渐进式引入。随着 Python 版本的演进,类型系统越来越强大和易用,值得深入学习和实践。
正文完
发表至: python
2026-02-12