Python 类型注解完全指南:写出更健壮的代码
什么是类型注解
类型注解是 Python 3.5+ 引入的特性,允许我们在函数参数、返回值和变量上标注预期的类型。这些注解在运行时不会强制执行,但可以被静态类型检查器(如 mypy)使用。
from typing import List, Dict, Optional
def process_data(items: List[int], threshold: int = 10) -> Dict[str, int]:
"""处理数据并返回统计结果"""
result = {"total": 0, "above_threshold": 0}
for item in items:
result["total"] += 1
if item > threshold:
result["above_threshold"] += 1
return result
# 使用示例
data = [5, 15, 25, 8, 12]
stats = process_data(data, 10)
print(stats) # 输出: {'total': 5, 'above_threshold': 3}
基础类型注解
常用类型
# 基础类型
def greet(name: str, age: int, is_active: bool) -> str:
return f"Hello {name(name)}, you are {age} years old"
# 可选类型
def get_user_name(user_id: int) -> Optional[str]:
users = {1: "Alice", 2: "Bob"}
return users.get(user_id)
# 联合类型
def process_value(value: int | str) -> str:
if isinstance(value, int):
return f"Number: {value}"
return f"Text: {value}"
集合类型
from typing import List, Dict, Set, Tuple
# 列表
def sum_numbers(nums: List[float]) -> float:
return sum(nums)
# 字典
def group_by_parity(numbers: List[int]) -> Dict[bool, List[int]]:
result = {True: [], False: []}
for num in numbers:
result[num % 2 == 0].append(num)
return result
# 集合
def unique_words(text: str) -> Set[str]:
return set(text.lower().split())
# 元组
def get_coordinates() -> Tuple[float, float, float]:
return (1.0, 2.0, 3.0)
高级类型注解
泛型与类型变量
from typing import TypeVar, Generic, List
T = TypeVar('T') # 泛型类型变量
def first_element(items: List[T]) -> T | None:
"""返回列表的第一个元素,保留类型信息"""
return items[0] if items else None
# 使用示例
numbers = [1, 2, 3]
first_num = first_element(numbers) # 类型推断为 int | None
words = ["hello",) "world"]
first_word = first_element(words) # 类型推断为 str | None
自定义类型与 NewType
from typing import NewType
# 创建语义化的类型别名
UserId = NewType('UserId', int)
UserName = NewType('UserName', str)
def get_user_info(user_id: UserId) -> UserName:
"""获取用户信息"""
# 类型检查器会确保传入的是 UserId 类型
return UserName("Alice")
# 使用
user = get_user_info(UserId(123)) # ✅ 正确
# get_user_info(123) # ❌ 类型检查器会警告
Callable 和 Protocol
from typing import Callable, Protocol
# Callable: 可调用对象类型
def apply(操作(value: int) -> int:
"""对整数应用操作并返回结果"""
return op(value)
# 示例:使用 lambda
函数 result = apply操作(10, lambda x: x * 2) # 结果: 20
# Protocol: 鸭子类型的类型注解
class Drawable(Protocol):
def draw(self) -> None: ...
def render(obj: Drawable) -> None:
obj.draw()
class Circle:
def draw(self) -> None:
print("Drawing a circle")
render(Circle()) # ✅ 符合 Protocol
类型注解的最佳实践
1. 渐进式采用
不要一次性给所有代码添加类型注解。从公共 API 开始,逐步完善:
# 优先为公共接口添加类型注解
def public_api(data: List[Dict[str, int]]) -> Dict[str, float]:
# 内部实现可以暂时不加类型
return {"average": sum(d["value"] for d in data) / len(data)}
# 内部辅助函数可以简略
def _helper(x):
return x * 2
2. 使用类型别名简化复杂类型
from typing import TypedDict, List
class UserProfile(TypedDict):
id: int
name: str
email: str
preferences: Dict[str, bool]
# 使用类型别名
UserList = List[UserProfile]
def filter_active_users(users: UserList) -> UserList:
return [u for u in users if u["preferences"].get("active", False)]
3. 配合 mypy 进行静态检查
# 保存为 type_check_example.py
def divide(a: int, b) -> float:
return a / b
# 运行 mypy: mypy type_check_example.py
# mypy 会检查类型一致性
4. 避免 over-specification
# ❌ 过于具体
def process(items: list[int]) -> list[int]:
return [x * 2 for x in items]
# ✅ 使用 Sequence 更灵活
from typing import Sequence
def process(items: Sequence[int]) -> list[int]:
return [x * 2 for x in items]
# 现在 process 可以接受 list、tuple、str 等任何序列类型
实战案例:构建类型安全的 API 客户端
from typing import TypedDict, Optional, Literal
import requests
class APIResponse(TypedDict):
status: int
data: dict
message: str
class User(TypedDict):
id: int
username: str
email: str
class APIClient:
def __init__(self, base_url: str) -> None:
self.base_url = base_url.rstrip('/')
def get_user(self, user_id: int) -> User:
"""获取用户信息"""
response = requests.get(f"{self.base_url}/users/{user_id}")
data = response.json()
return User(
id=data["id"],
username=data["username"],
email=data["email"]
)
def create_user(self, username: str, email: str) -> APIResponse:
"""创建新用户"""
payload = {"username": username, "email": email}
response = requests.post(f"{self.base_url}/users", json=payload)
return APIResponse(
status=response.status_code,
data=response.json(),
message="User created" if response.ok else "Failed"
)
# 使用示例
client = APIClient("https://api.example.com")
user = client.get_user(123) # 类型:User
总结
Python 类型注解不是强制执行类型的约束,而是提升代码质量的辅助工具。主要优势包括:
- 更好的 IDE 支持:自动补全、参数提示、类型检查
- 文档作用:类型注解本身就是活文档
- 减少 bug:静态检查可以在运行前发现潜在问题
- 团队协作:统一的类型约定降低沟通成本
建议在新项目中积极使用类型注解,老项目可以渐进式引入。配合 mypy、IDE 的类型检查功能,可以显著提升开发体验和代码质量。
进一步学习
- 安装 mypy:
pip install mypy - 运行类型检查:
mypy your_module.py - Python typing 官方文档:https://docs.python.org/3/library/typing.html
