Python typing 模块常用类型提示工具
Python typing 模块常用类型提示工具
- 每个工具都补充 典型使用场景、优势 / 劣势
- 所有示例均带 中文注释、可直接运行(Python 3.10+,如用 3.8/3.9 需把
|换成Union)。
1. 基本标量类型(int、str …)
def add(a: int, b: int) -> int:
"""
场景:业务逻辑里最常见的数值运算。
优势:零成本、IDE 可推断、静态分析器 100% 支持。
劣势:无法表达“正整数”“非空字符串”等更细粒度约束。
"""
return a + b
2. 泛型容器 list / dict / set / tuple
from typing import Dict
def freq(words: list[str]) -> Dict[str, int]:
"""
场景:处理同构集合(元素类型一致)。
优势:写法直观;mypy/pyright 能发现“把 int 放进 list[str]”这类错误。
劣势:运行时仍是原生容器,不检查具体元素;无法限制 list 长度。
"""
counts: Dict[str, int] = {}
for w in words:
counts[w] = counts.get(w, 0) + 1
return counts
3. Union 与 3.10+ |
from typing import Union # 3.10 起可写 int | float
def parse_number(text: str) -> int | float:
"""
场景:函数可能返回多种类型,调用方必须“类型收窄”。
优势:静态检查可提醒“你忘了处理 float 分支”。
劣势:返回值变“大 Union”,调用者需 isinstance 判断,易遗漏。
"""
try:
return int(text)
except ValueError:
return float(text)
4. Optional[T](Union[T, None] 的语法糖)
from typing import Optional
def find_user(uid: str) -> Optional[User]:
"""
场景:查询类函数“找不到”时返回 None。
优势:显式告诉调用者要做空值检查,减少 NoneType 错误。
劣势:容易写出“Optional[Optional[T]]”这种套娃;None 语义弱,无法区分“不存在”还是“空结果”。
"""
...
5. TypeVar(泛型函数 / 类)
from typing import TypeVar, Sequence
T = TypeVar("T") # 无界泛型
S = TypeVar("S", int, str) # 限定为 int 或 str
def first(items: Sequence[T]) -> T | None:
"""
场景:编写与元素类型无关的通用算法(如 first、last、chunk)。
优势:保持 API 类型安全,调用方拿到精确类型。
劣势:过度泛型会降低可读性;协变/逆变规则对初学者不友好。
"""
return items[0] if items else None
6. Callable(高阶函数 / 回调)
from typing import Callable
def retry(
fn: Callable[[str], bool],
times: int = 3
) -> bool:
"""
场景:装饰器、事件总线、策略模式。
优势:明确参数 & 返回值;IDE 可给出补全和跳转。
劣势:无法描述“关键字参数”、“可变参数”签名;复杂签名可读性差。
"""
for _ in range(times):
if fn("ping"):
return True
return False
7. Protocol(结构化子类型 / 鸭子类型)
from typing import Protocol, Iterator
class Readable(Protocol):
def read(self, size: int = -1) -> bytes: ...
def stream_hash(reader: Readable) -> str:
"""
场景:想接受“任何有 read() 方法的对象”,但不想继承公共基类。
优势:解耦、符合鸭子类型;支持静态检查。
劣势:Protocol 匹配基于“结构”,运行时仍可能因缺少属性抛错;多重继承冲突时调试困难。
"""
hasher = hashlib.sha256()
for chunk in iter(lambda: reader.read(8192), b""):
hasher.update(chunk)
return hasher.hexdigest()
8. TypedDict(半结构化 JSON / dict)
from typing import TypedDict, Required, NotRequired
class Movie(TypedDict, total=False):
name: Required[str]
year: int
tags: list[str]
def to_xml(m: Movie) -> str:
"""
场景:处理外部 JSON、ORM 字典行、配置文件。
优势:字段级类型提示;配合 mypy --strict 可检测多余/缺失键。
劣势:仍是 dict,运行时无强制校验;字段可选/必选、只读等高级标记语法冗长。
"""
...
9. NewType(轻量级“品牌类型”)
from typing import NewType
UserId = NewType("UserId", int) # 运行时仍是 int,静态上视为不同类型
OrderId = NewType("OrderId", int)
def cancel_order(uid: UserId, oid: OrderId) -> None:
"""
场景:防止“把用户 ID 当订单 ID”这种语义混淆。
优势:零运行时开销;静态检查可发现混用。
劣势:运行时无法区分,序列化/调试时打印仍是裸 int;需要额外适配 ORM / JSON。
"""
...
10. Final(常量、禁止继承 / 重写)
from typing import Final
MAX_RETRY: Final[int] = 3
class Config:
DEBUG: Final[bool] = False
class Bad(Config):
DEBUG = True # mypy: Cannot assign to final name "DEBUG"
场景:定义全局常量、防止子类意外覆盖。
优势:静态检查 + 运行时 typing.final 装饰器双重保险。
劣势:仅提示,无法真正“锁定”值;与元编程(动态赋值)冲突。
11. Literal(枚举式取值)
from typing import Literal
LogLevel = Literal["DEBUG", "INFO", "WARNING", "ERROR"]
def setup_logger(level: LogLevel) -> None:
"""
场景:函数只接受有限的字符串/整数字面量。
优势:比 Enum 轻量;静态检查可发现拼写错误。
劣势:运行时仍是普通字符串;不能附加额外元数据(描述、值映射)。
"""
...
12. TypeGuard(自定义类型收窄函数)
from typing import TypeGuard
def is_str_list(val: list[object]) -> TypeGuard[list[str]]:
"""
场景:自己写 isinstance 逻辑,让 mypy/pyright 理解“已收窄”。
优势:消除大量显式 cast;逻辑集中一处。
劣势:只在静态检查有效,运行时仍是普通函数;复杂判断可能降低性能。
"""
return all(isinstance(x, str) for x in val)
def concat(data: list[object]) -> str:
if is_str_list(data):
# mypy 在此分支把 data 视为 list[str]
return "".join(data)
raise ValueError("not all strings")
13. Annotated(把元数据附着在类型上)
from typing import Annotated
from fastapi import Query
def search(
q: Annotated[str, Query(min_length=3, max_length=50)]
) -> list[str]:
"""
场景:FastAPI/Pydantic/数据库驱动读取字段元数据。
优势:不破坏类型本身,仅附加约束;工具链可统一消费。
劣势:标准库无运行时校验,需要额外库解析;IDE 提示信息可能过长。
"""
...
14. TypeAlias / TypeAliasType(3.12+)
from typing import TypeAlias
# 旧写法
Vector: TypeAlias = tuple[float, ...]
# 3.12+ 新写法
type Matrix = list[Vector]
def scale(m: Matrix, k: float) -> Matrix:
"""
场景:复杂嵌套类型需要可读名字。
优势:IDE 跳转、文档化更清晰;3.12 起支持泛型 TypeAliasType。
劣势:旧工具链可能未支持新语法。
"""
return [[k * x for x in row] for row in m]
15. Any / NoReturn / Never
from typing import Any, NoReturn
def log(obj: Any) -> None:
"""
场景:与动态语言交互、迁移旧代码、调试打印。
优势:快速关闭类型检查,减少红色波浪线。
劣势:一旦滥用,静态检查形同虚设;重构时无法获得帮助。
"""
def abort(msg: str) -> NoReturn:
"""
场景:永远抛异常 / 退出进程。
优势:告诉类型检查器“后续代码不可达”,避免“可能未初始化”误报。
劣势:仅静态信息,运行时仍需手动 raise/exit。
"""
raise SystemExit(msg)
小结:如何选型
| 需求 | 首选 | 慎用 |
|---|---|---|
| 同构集合 | list[int] |
List(3.8-) |
| 可选值 | T | None |
多层 Optional |
| 鸭子类型 | Protocol |
Any |
| 结构字典 | TypedDict |
dict[str, Any] |
| 语义区分 | NewType |
裸 int/str |
| 有限取值 | Literal |
字符串魔法值 |
| 元数据 | Annotated |
注释 + 运行时手动解析 |
把这张表与示例代码放进团队 Wiki,就能让类型提示既“写得爽”又“跑得稳”。
浙公网安备 33010602011771号