Python 作为动态类型语言,灵活性高但类型安全性不足。类型注解(Type Hints)自 Python 3.5 引入,允许开发者为变量、函数参数和返回值添加类型信息,从而提升代码可读性、IDE 支持、静态类型检查、文档质量和团队协作效率。本文从基础到高级应用全面解析 Python 类型注解,涵盖变量注解、函数注解、泛型、联合类型、TypedDict、数据类、回调函数、协议、类型检查工具及实际 Web API 和异步代码示例。
一、基础类型注解
变量注解:- name: str = "Alice"
- age: int = 25
- height: float = 1.75
- is_student: bool = True
- # 容器类型需从 typing 导入
- from typing import List, Dict, Tuple, Set
- numbers: List[int] = [1, 2, 3, 4, 5]
- user_data: Dict[str, str] = {"name": "Bob", "email": "bob@example.com"}
- coordinates: Tuple[float, float] = (10.5, 20.3)
- unique_ids: Set[int] = {1, 2, 3, 4}
- # Python 3.10+ 可选类型简化
- maybe_name: str | None = None # 等价于 Optional[str]
复制代码
函数注解:- def greet(name: str) -> str:
- return f"Hello, {name}!"
- def calculate_stats(numbers: List[float]) -> Tuple[float, float, float]:
- if not numbers:
- return 0.0, 0.0, 0.0
- return sum(numbers)/len(numbers), min(numbers), max(numbers)
- def create_user(username: str, email: str, age: int = 18, is_active: bool = True) -> Dict[str, any]:
- return {"username": username, "email": email, "age": age, "is_active": is_active}
复制代码
二、高级类型注解
泛型(Generic Types)允许编写可复用的类型安全组件。通过 TypeVar 和 Generic 实现:- from typing import TypeVar, Generic, List
- T = TypeVar('T')
- 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 is_empty(self) -> bool:
- return len(self._items) == 0
- int_stack: Stack[int] = Stack()
- str_stack: Stack[str] = Stack()
复制代码
联合类型与可选类型:- from typing import Union, Optional, Any, List, Dict
- def process_value(value: Union[int, str, List[int]]) -> str:
- if isinstance(value, int):
- return f"整数: {value}"
- elif isinstance(value, str):
- return f"字符串: {value}"
- else:
- return f"列表: {value}"
- def find_user(user_id: int) -> Optional[Dict[str, Any]]:
- users = {1: {"name":"Alice","age":25}, 2: {"name":"Bob","age":30}}
- return users.get(user_id)
- # Python 3.10+ 简化语法
- from typing import Union # 或直接用 |
- def process_data(data: int | str | None) -> str:
- if data is None:
- return "无数据"
- return f"数据: {data}"
复制代码
类型别名与 NewType:类型别名提高复杂类型可读性,NewType 创建语义不同的子类型,用于严格类型区分。- UserId = int
- UserName = str
- def get_user_name(user_id: UserId) -> UserName:
- return f"User{user_id}"
- from typing import NewType
- UserId = NewType('UserId', int)
- AdminId = NewType('AdminId', int)
- def create_user(user_id: UserId) -> None: ...
- def create_admin(admin_id: AdminId) -> None: ...
- user_id = UserId(123)
- admin_id = AdminId(456)
- create_user(user_id) # OK
- create_user(123) # 类型检查器警告
复制代码
三、结构化类型注解
TypedDict 用于定义字典结构,支持可选字段(NotRequired):- from typing import TypedDict, NotRequired
- class UserProfile(TypedDict):
- username: str
- email: str
- age: int
- is_active: bool
- bio: NotRequired[str]
- def update_profile(profile: UserProfile) -> None:
- print(f"更新用户: {profile['username']}")
- user: UserProfile = {"username":"alice","email":"a@e.com","age":25,"is_active":True}
复制代码
数据类(Dataclass)结合类型注解:- from dataclasses import dataclass, field
- from typing import ClassVar, Final, List
- @dataclass
- class Product:
- name: str
- price: float
- description: str = ""
- tags: List[str] = field(default_factory=list)
- tax_rate: ClassVar[float] = 0.1
- MAX_PRICE: Final[float] = 10000.0
- def total_price(self, quantity: int = 1) -> float:
- if self.price > self.MAX_PRICE:
- raise ValueError(f"价格不能超过{self.MAX_PRICE}")
- return self.price * quantity * (1 + self.tax_rate)
- product = Product(name="笔记本电脑", price=5000.0, description="高性能", tags=["电子"])
复制代码
四、回调函数与高阶函数类型注解
- from typing import Callable, List
- NumberTransformer = Callable[[float], float]
- def apply_transform(numbers: List[float], transform: NumberTransformer) -> List[float]:
- return [transform(n) for n in numbers]
- def double(x: float) -> float: return x*2
- def square(x: float) -> float: return x**2
- apply_transform([1.0,2.0], double)
复制代码
装饰器类型注解:- from functools import wraps
- def log_execution(func: Callable) -> Callable:
- @wraps(func)
- def wrapper(*args, **kwargs):
- print(f"执行函数: {func.__name__}")
- result = func(*args, **kwargs)
- return result
- return wrapper
- @log_execution
- def calculate_sum(a: int, b: int) -> int: return a+b
复制代码
五、协议(Protocol)与结构化子类型
Protocol 允许定义结构类型,无需继承即可实现接口,类似 Go 的隐式实现。- from typing import Protocol, List
- @runtime_checkable
- class Drawable(Protocol):
- def draw(self) -> None: ...
- @property
- def area(self) -> float: ...
- class Circle:
- def __init__(self, radius: float): self.radius = radius
- def draw(self) -> None: print(f"绘制圆形,半径: {self.radius}")
- @property
- def area(self) -> float: return 3.14159 * self.radius**2
- class Rectangle:
- def __init__(self, width, height): self.width=width; self.height=height
- def draw(self): print(f"绘制矩形,宽:{self.width},高:{self.height}")
- @property
- def area(self): return self.width*self.height
- def render_shapes(shapes: List[Drawable]) -> None:
- for shape in shapes:
- shape.draw()
- print(f"面积: {shape.area}")
- render_shapes([Circle(5.0), Rectangle(4.0,6.0)])
复制代码
六、类型检查工具
Mypy 基础使用:- pip install mypy
- mypy your_script.py
- mypy --strict your_script.py
- mypy --html-report report/ your_project/
复制代码
pyright 配置文件(pyrightconfig.json):- {
- "include": ["src"],
- "exclude": ["**/__pycache__", "**/.pytest_cache"],
- "typeCheckingMode": "strict",
- "pythonVersion": "3.10",
- "reportMissingImports": true,
- "reportUnusedImport": true
- }
复制代码
代码中忽略类型检查:- from typing import cast, Any, no_type_check
- value: Any = get_data() # type: ignore
- result: int = value + 10
- typed_data = cast(List[int], data)
- @no_type_check
- def legacy_function(x):
- return x + "字符串"
复制代码
七、实际应用示例
Web API 使用 FastAPI + Pydantic 类型注解:- from fastapi import FastAPI, Query, Path
- from pydantic import BaseModel, Field
- from typing import Annotated
- app = FastAPI()
- class UserCreate(BaseModel):
- username: str = Field(..., min_length=3, max_length=50)
- email: str = Field(..., regex=r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$")
- age: int = Field(..., ge=0, le=150)
- tags: list[str] = Field(default_factory=list)
- class UserResponse(BaseModel):
- id: int
- username: str
- email: str
- age: int
- created_at: str
- @app.post("/users/", response_model=UserResponse)
- async def create_user(user: UserCreate) -> UserResponse:
- return UserResponse(id=1, username=user.username, email=user.email, age=user.age, created_at="2024-01-01T00:00:00")
- @app.get("/users/{user_id}")
- async def get_user(user_id: Annotated[int, Path(title="用户ID", ge=1)], include_details: Annotated[bool, Query(False)] = False) -> dict[str, Any]:
- return {"id": user_id, "username": "test_user", "include_details": include_details}
复制代码
异步代码类型注解:- import asyncio
- from typing import AsyncIterator, Awaitable
- from contextlib import asynccontextmanager
- async def fetch_data(url: str) -> dict[str, Any]:
- await asyncio.sleep(0.1)
- return {"url": url, "data": "示例数据"}
- async def process_items(items: list[str]) -> AsyncIterator[str]:
- for item in items:
- await asyncio.sleep(0.01)
- yield f"处理: {item}"
- @asynccontextmanager
- async def database_connection(db_url: str) -> AsyncIterator[dict]:
- connection = {"url": db_url, "connected": True}
- try:
- yield connection
- finally:
- connection["connected"] = False
- async def main() -> None:
- urls = ["https://api.example.com/data1", "https://api.example.com/data2"]
- tasks: list[Awaitable[dict]] = [fetch_data(url) for url in urls]
- results = await asyncio.gather(*tasks)
- async with database_connection("postgresql://localhost/db") as db:
- print(f"连接到数据库: {db['url']}")
- async for result in process_items(["item1","item2"]):
- print(result)
- if __name__ == "__main__":
- asyncio.run(main())
复制代码
八、最佳实践与常见陷阱
最佳实践:渐进式采用,从关键函数开始;保持项目内注解风格一致;使用类型别名和泛型提高可读性和复用性;定期运行类型检查并集成到 CI/CD。
常见陷阱:
1. 过度使用 Any:应使用 TypeVar 或具体类型。
2. 忽略容器元素类型:应写 list[str] 而非 list。
3. 循环引用:使用字符串字面量或 from __future__ import annotations。
- # 正确做法:字符串字面量
- class TreeNode:
- def __init__(self, children: list["TreeNode"]): ...
- # 或使用 future
- from __future__ import annotations
- class TreeNode:
- def __init__(self, children: list[TreeNode]): ...
复制代码
九、未来发展方向
Python 类型注解持续演进,3.10+ 简化联合语法,3.11+ 增加 Self 类型,3.12+ 强化参数规范。开发者应紧跟版本更新,合理利用特性提升代码健壮性。
通过本文的系统梳理,你可以从基础变量注解到高级泛型、TypedDict、Protocol、异步代码类型检查全面掌握 Python 类型注解,并在实际项目中有效应用。 |