
当函数返回 `union[int, str]` 类型时,直接将其赋值给 `int` 或 `str` 变量会触发 mypy 类型检查错误;需通过类型断言(`cast`)或运行时类型检查(`isinstance`)显式告知类型系统实际类型。
在静态类型检查工具(如 MyPy)中,函数声明返回 Union[int, str](或等价的 int | str)意味着调用方必须处理两种可能类型。即使你根据输入逻辑「确定」某次调用只返回 int(如 a(1)),MyPy 仍会保守地将表达式类型视为完整联合类型,因此以下赋值会报错:
c: int = a(1) # ❌ error: Incompatible types in assignment d: str = a(0) # ❌ error: Incompatible types in assignment
✅ 方案一:使用 cast() 进行类型断言(简洁但需谨慎)
cast() 是一种“告诉类型检查器:我比你更清楚这个值的实际类型”的方式,它不改变运行时行为,仅影响类型推导:
from typing import cast c = cast(int, a(1)) # ✅ MyPy 接受:c 被视为 int d = cast(str, a(0)) # ✅ MyPy 接受:d 被视为 str
⚠️ 注意事项:
- cast 完全绕过类型安全验证——若断言错误(如 cast(str, a(1))),MyPy 不报错,但运行时可能引发逻辑错误;
- 适合逻辑确定、调用上下文高度可控的场景(如单元测试、配置驱动分支);
- 建议配合注释说明断言依据,例如:c = cast(int, a(1)) # b=1 → always returns int.
✅ 方案二:使用 isinstance() 运行时类型检查(安全且推荐)
这是符合 Python 类型哲学的首选方式:让代码显式处理联合类型的歧义,并保留完整的类型安全:
result = a(1)
if isinstance(result, int):
c: int = result # ✅ MyPy infers `result` as `int` in this branch
process_int(c)
elif isinstance(result, str):
d: str = result # ✅ MyPy infers `result` as `str` in this branch
process_str(d)你甚至可以结合类型守卫(Type Guard)提升可读性:
def is_int(x: object) -> TypeGuard[int]:
return isinstance(x, int)
val = a(1)
if is_int(val):
c = val # ✅ MyPy knows val is int✅ 优势:
- 无运行时风险,类型分支与实际逻辑一致;
- 支持后续对不同分支调用类型特化函数(如 function_taking_int(c));
- 符合 PEP 484 和 MyPy 的最佳实践。
? 总结建议
| 场景 | 推荐方案 | 理由 |
|---|---|---|
| 快速原型/已知确定分支 | cast() | 简洁,减少样板代码 |
| 生产代码、需长期维护 | isinstance() 分支 | 类型安全、可读性强、易于调试 |
| 多重类型判断或复杂逻辑 | match(Python 3.10+)或自定义 TypeGuard | 更具扩展性和表现力 |
最后提醒:避免过度依赖 cast 掩盖设计问题。如果函数频繁需要被强制断言,应考虑重构——例如拆分为多个明确返回单一类型的函数(a_int(b: int) -> int / a_str(b: int) -> str),或使用 @overload 提供更精确的调用签名。










