typing
问题
Python 的类型注解系统有哪些核心概念?Generic、Protocol、TypeVar 怎么用?
答案
基础类型注解
# 基本类型(3.9+ 可直接用内置类型)
name: str = "Alice"
scores: list[int] = [90, 85, 92]
config: dict[str, int] = {"timeout": 30}
result: tuple[str, int] = ("ok", 200) # 固定长度
# 可选值
from typing import Optional
age: Optional[int] = None # 等价于 int | None(3.10+)
# 联合类型
from typing import Union
value: Union[int, str] = 42 # 3.10+ 可写 int | str
# 可调用
from typing import Callable
handler: Callable[[int, str], bool] = lambda x, s: True
TypeVar 和 Generic
from typing import TypeVar, Generic
T = TypeVar("T")
# 泛型函数
def first(items: list[T]) -> T:
return items[0]
# 泛型类
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()
stack: Stack[int] = Stack()
stack.push(42)
# Python 3.12+ 新语法
def first[T](items: list[T]) -> T: # 无需 TypeVar
return items[0]
Protocol — 结构化子类型
from typing import Protocol
class Drawable(Protocol):
def draw(self) -> None: ...
class Circle:
def draw(self) -> None:
print("画圆")
# Circle 没有显式继承 Drawable,但满足协议(鸭子类型的类型化)
def render(shape: Drawable) -> None:
shape.draw()
render(Circle()) # ✅ 类型检查通过
常用工具类型
| 类型 | 用途 | 示例 |
|---|---|---|
TypeAlias | 类型别名 | Vector: TypeAlias = list[float] |
Literal | 字面量类型 | mode: Literal["r", "w"] |
TypedDict | 字典结构约束 | class Config(TypedDict): ... |
Final | 不可重新赋值 | MAX: Final = 100 |
Annotated | 附加元数据 | Age = Annotated[int, Gt(0)] |
Self (3.11+) | 返回自身类型 | def copy(self) -> Self: ... |
TypeGuard | 类型收窄 | def is_str(x) -> TypeGuard[str] |
常见面试问题
Q1: 类型注解会影响运行时吗?
答案:
不会。Python 的类型注解只是元数据,不影响运行时行为。它们服务于:
- IDE 提示和自动补全
- 静态类型检查器(mypy、Pyright)
- 文档生成
- 框架运行时使用(如 Pydantic、FastAPI 用注解做验证)
Q2: Protocol 和 ABC 的区别?
答案:
| 对比 | Protocol | ABC |
|---|---|---|
| 子类型方式 | 结构化(鸭子类型) | 名义化(显式继承) |
| 需要继承 | ❌ | ✅ |
| 运行时检查 | ❌(默认) | ✅(isinstance) |
| 适用场景 | 第三方代码 | 自己的类体系 |