
本文介绍解决 mypy 报错 “incompatible types in assignment for function return union of types” 的两种主流方案:使用 `cast()` 进行显式类型断言,或通过 `isinstance()` 做运行时类型检查,兼顾类型安全与代码可维护性。
当函数返回 Union[int, str](或 Python 3.10+ 的简写 int | str),而你试图将调用结果直接赋值给单一类型变量(如 c: int = a(1))时,MyPy 会报错——因为静态分析无法保证每次调用都返回预期子类型。这不是 bug,而是类型系统在强制你显式表达“此处我确信结果是 int/str”。
✅ 方案一:使用 cast()(适用于确定性场景)
当你完全掌握输入与返回类型的映射逻辑(例如 a(1) 必然返回 int,a(0) 必然返回 str),可借助 typing.cast 告诉 MyPy:“请信任我,这个表达式的实际类型就是指定类型”。
from typing import cast
def a(b: int) -> int | str:
if b:
return b
else:
return "2"
c = cast(int, a(1)) # MyPy 接受:c 被视为 int
d = cast(str, a(0)) # MyPy 接受:d 被视为 str⚠️ 注意:cast() 是零运行时代价的类型提示工具,不进行任何运行时检查。若断言错误(如 cast(str, a(1))),MyPy 不会报错,但可能引发后续逻辑异常。务必确保断言与业务逻辑严格一致。
✅ 方案二:使用 isinstance()(推荐用于健壮性优先场景)
更安全、更符合 Python 类型哲学的方式是:先接收联合类型,再通过运行时类型检查分支处理。MyPy 能识别 isinstance(x, int) 后的类型窄化(type narrowing),自动推导分支内变量类型:
def a(b: int) -> int | str:
if b:
return b
else:
return "2"
result = a(1) # result: int | str
if isinstance(result, int):
print("Got int:", result + 10) # result 在此分支中被推导为 int
elif isinstance(result, str):
print("Got str:", result.upper()) # result 在此分支中被推导为 str该方式无需额外依赖,类型安全由运行时保障,且 MyPy 能提供精准的分支内类型推导,适合不确定输入行为或需防御性编程的场景。
? 总结建议
- 优先选择 isinstance():尤其在函数逻辑可能变化、或输入来源不可控时,它兼具类型安全与可读性;
- 谨慎使用 cast():仅限于性能敏感、逻辑绝对稳定(如配置驱动的固定分支)、且团队明确约定的场景;
- 长期来看,也可考虑重构函数为多个明确签名的重载函数(@overload),让接口契约更清晰——但这会增加 API 复杂度,需权衡。
类型系统是你的协作者,而非障碍;关键是在“精确表达意图”和“保持代码健壮”之间找到平衡点。










