python 3.10起|运算符正式替代union,int | str等价于union[int, str],是pep 604引入的原生类型系统特性,生成types.uniontype实例,无需导入且更简洁,union已弃用。

Python 3.10 的 | 联合类型语法可以直接替代 Union[]
Python 3.10 正式支持用 | 作为类型联合运算符,比如 int | str 等价于 Union[int, str]。它不是语法糖——解释器原生识别,且在运行时是合法的表达式(只要两边是类型),但注意:仅当 from __future__ import annotations 未启用时,| 在运行时才真正被解析为类型联合;否则它只是字符串字面量(PEP 604 + PEP 563 共同作用)。
常见错误现象:TypeError: unsupported operand type(s) for |: 'type' and 'type',这通常发生在 Python int | str,或虽为 3.10+ 但启用了 from __future__ import annotations 后又试图在运行时做类型检查(如传给 isinstance 或手动求值)。
- 必须确保 Python 版本 ≥ 3.10(
sys.version_info >= (3, 10)) - 若使用
from __future__ import annotations(推荐用于避免循环引用),则int | str不会在运行时构造Union对象,而是保留为字符串;此时需靠类型检查器(如 mypy、pyright)理解语义,不能依赖typing.get_origin()等运行时 API -
None必须显式写成None,不能简写为NoneType;可选类型统一用str | None,而非Optional[str](后者仍可用,但已不推荐)
函数参数和返回值中用 | 声明联合类型的实际写法
这是最常用场景,也是最容易出错的地方:参数注解、返回值注解、变量注解都支持 |,但要注意嵌套和优先级。
示例:
def parse_value(s: str) -> int | float | None:
if not s:
return None
try:
return int(s)
except ValueError:
return float(s)
<p>x: list[str | bytes] = ["a", b"b"] # ✅ 正确:括号必要
y: dict[str, int | bool] = {"a": 1, "b": True} # ✅ 正确
z: str | int | list[bool] = [True] # ✅ 正确
</p>
-
|是左结合、低优先级运算符,str | int | list[bool]等价于(str | int) | list[bool],无需额外括号 - 但容器类型内部必须加括号,比如
list[str | bytes]—— 写成list[str | bytes]是对的,而list[str] | bytes就是“列表或字节”,不是“列表元素为字符串或字节” - 与
Literal、Callable等复合类型混用时,务必用括号明确范围,例如Callable[[], int | str]✅,Callable[[], int] | str❌(含义完全不同)
和 Union 混用、兼容性及 mypy/pyright 行为差异
虽然 | 是新标准,但 Union 并未被弃用,两者在类型检查器中基本等价。不过细节上仍有坑:
- mypy 默认接受
int | str和Union[int, str],但若项目中混合使用,其错误提示可能把二者归一化显示为Union形式,容易让人误以为|被“转译”了 - pyright(VS Code 默认 LSP)对
|支持更激进,甚至允许int | str | None自动推导为Optional[int | str],但 mypy 不会这样简化 - 第三方库(如
pydantic v1)不识别|,会报TypeError: unsupported operand type(s) for |;pydantic v2已完全支持,但需确认typing_extensions版本 ≥ 4.0.0(为旧 Python 补充 PEP 604 支持) - 运行时反射类(如
dataclasses.fields()获取的Field.type)在 3.10+ 中可能返回types.UnionType实例(而非typing.Union),需用typing.get_origin(x) is types.UnionType判断,而不是硬比较== Union
什么时候不该用 |?几个隐蔽但关键的限制
| 看似方便,但在某些上下文中它不合法或不适用,强行使用会导致语法错误或语义偏差。
- 不能用于
isinstance()或issubclass()的第二个参数:例如isinstance(x, int | str)会报SyntaxError(3.10+ 也不行),必须写成isinstance(x, (int, str)) - 不能出现在
__annotations__字典的原始值中(如果没启用from __future__ import annotations):此时解释器尝试求值int | str,但int和str是内置类型,不支持位或操作,直接崩溃 - 泛型别名定义中若含
|,需配合typing.TypeAlias(3.12+)或保持为字符串(3.10–3.11);例如StrOrInt = str | int在模块顶层是合法的,但若放在函数内,则该变量不会被类型检查器识别为类型别名 -
typing.NamedTuple字段注解里用|没问题,但collections.namedtuple不支持类型注解,自然也不支持|
最常被忽略的一点:| 是类型系统层面的语法,它不改变运行时行为,也不参与对象创建或转换。写 def f(x: int | str) 并不会让 x 自动转成 int 或 str,它只告诉类型检查器“这里接受这两种类型之一”。实际逻辑仍需你自己用 isinstance 或 try/except 处理。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











