Python 3.9起str | int成为合法类型联合语法,等价于Union[str, int];Optional[T]可直接写作T | None;字典合并|与类型联合|由上下文区分,互不冲突。

| 运算符在类型提示中不是 Python 3.9 引入的新东西——它早在 Python 3.5 就存在,且一直用于表示联合类型(比如 Union[str, int] 等价于 str | int)。真正从 3.9 开始「可用」的,是字典合并运算符 | 和 |=。很多人混淆,是因为两个功能共用了同一个符号,但语义和使用场景完全不同。
下面直接说清楚你实际会遇到的问题点:
为什么 str | int 在 Python 3.9 才能用?
因为 | 作为类型联合操作符,依赖语言层面支持「中缀运算符重载用于类型构造」,这在 3.9 之前不被语法允许。虽然 Union[str, int] 一直可用,但 str | int 直到 3.9 才成为合法语法。
- Python 3.8 及更早:写
str | int会报SyntaxError: invalid syntax - Python 3.9+:
str | int是合法、推荐的写法,等价于Union[str, int] - 注意:必须开启类型检查器支持(如 mypy ≥ 0.900 或 pyright),否则 IDE 可能不识别
Optional[T] 能否直接写成 T | None?
可以,而且从 3.9 开始这是官方鼓励的写法。因为 Optional[T] 本质就是 Union[T, None],而 Union[T, None] 又可简写为 T | None。
立即学习“Python免费学习笔记(深入)”;
- 旧写法:
from typing import Optional+def f(x: Optional[str]) -> None: - 新写法:
def f(x: str | None) -> None:,无需导入Optional - 坑点:如果项目还混用
Optional和| None,mypy 默认不会报错,但团队规范容易撕裂
字典合并 | 和类型联合 | 怎么不冲突?
Python 解析器靠上下文区分:左边是 dict 类型对象 → 当作合并;左边是类型名(如 str、list)→ 当作联合类型。两者互不影响。
- 类型上下文示例:
def g() -> dict[str, int | str]: ...—— 这里的|是类型联合 - 值上下文示例:
a = {'x': 1}; b = {'y': 2}; c = a | b—— 这里的|是字典合并 - 危险组合:
dict[str, int] | dict[str, float]是合法类型表达式(表示“这个变量是前者或后者”),但它和字典合并毫无关系
升级到 3.9 后,哪些类型写法必须改?
不需要强制改,但改了更干净。关键是别漏掉那些「看似没变,实则失效」的旧惯性写法。
-
List[int]、Dict[str, Any]等仍可用(向后兼容),但需继续from typing import List, Dict, Any - 若想用
list[int]、dict[str, Any],就必须用 Python 3.9+,且不能在运行时做类型检查(如isinstance(x, list[int])会报错) - 最常踩的坑:
tuple[int, ...]在 3.9+ 可写成tuple[int, ...](不变),但Tuple[int, ...]仍需导入,且tuple[int, ...]不支持tuple[int]这种单元素简写(必须写tuple[int,])
类型系统演进不是越新越要全切,而是看团队工具链是否跟得上。比如 CI 里用的 mypy 版本太低,str | int 就会直接报错;又比如有些老库的 stubs 还没适配内置泛型,硬切反而增加维护成本。真正该优先落地的,是那些既提升可读性、又无兼容风险的点:比如把满屏的 Optional[...] 换成 ... | None,一行删掉一个 import。


















