类型别名(Type Alias)在 Python typing 演进里是条很有意思的支线,因为它经历了 隐式别名 → 显式别名 → 新语法别名 三个阶段。
Python 类型别名的演变
1 最早:隐式类型别名(PEP 484时代)
最初根本没有专门语法。
只要把类型赋值给变量,就是别名:
Vector = list[float]
然后:
def magnitude(v: Vector) -> float:...
Vector 只是:
list[float]
的名字。
为什么需要别名
否则复杂类型会变成噩梦:
dict[str, list[tuple[int, str | None]]]
改成:
UserMap = dict[str, list[tuple[int, str | None]]]
可读性高很多。
JSON 类型经典例子
JSON = (dict[str, "JSON"]| list["JSON"]| str| int| float| bool| None
)
这个几乎是教科书案例。
2 问题:变量还是别名?
麻烦在于:
Config = dict[str, str]
这是:
- 类型别名?
还是:
- 一个变量值?
静态检查器要猜。
很不优雅。
3 PEP 613:TypeAlias(显式声明)
PEP 613
Python 3.10:
from typing import TypeAliasVector: TypeAlias = list[float]
现在明确告诉类型检查器:
这是 alias,不是变量。
好处一:消除歧义
以前:
MyType = "ClassName"
是字符串变量?
还是前向引用别名?
不清楚。
现在:
MyType: TypeAlias = "ClassName"
明确。
好处二:泛型别名
from typing import TypeVar, TypeAliasT = TypeVar("T")Vec: TypeAlias = list[T]
很漂亮。
好处三:类型检查更好
mypy / Pyright 能报更准确错误。
比如:
Alias: TypeAlias = 42
会报错。
因为不是合法类型。
4 PEP 695:新一代 type 语句(Python 3.12)
大升级。
PEP 695
现在:
type Vector = list[float]
不是赋值。
是真正类型声明。
像:
- TypeScript
type Vector = number[]
- Rust
type Vector = Vec<f64>;
Python 第一次有正式 type statement。
泛型别名更优雅
旧:
T = TypeVar("T")
Vec = list[T]
新:
type Vec[T] = list[T]
这个非常漂亮。
条件类型组合也更舒服
type Response[T] = T | Exception
递归别名
以前很痛苦:
JSON = dict[str, JSON] | ...
新语法支持更自然。
5 TypeAlias vs NewType(常被混淆)
这个必须讲。
TypeAlias
只是换名字:
type UserId = int
本质:
UserId is int
没区别。
NewType
NewType
from typing import NewTypeUserId = NewType("UserId", int)
这是静态层面新类型:
user: UserId
不能随便拿 ProductId 混进来。
举例
坏:
type UserId = int
type ProductId = int
类型检查器认为一样。
好:
UserId = NewType("UserId", int)
ProductId = NewType("ProductId", int)
不会混。
这很适合领域建模。
6 三代演化总结
| 阶段 | 写法 |
|---|---|
| PEP 484 | Vector = list[float] |
| PEP 613 | Vector: TypeAlias = list[float] |
| PEP 695 | type Vector = list[float] |
很明显是:
从约定
到显式标注
到语言语法
我个人建议博客里强调一点
类型别名不是“缩写”。
它本质是:
- 可读性工具
- 抽象边界
- 领域建模工具
比如:
type Money = Decimal
type Email = str
type UserRecord = dict[str, str | int]
这已经有 DSL 味道了。
