typeguard 是 python 3.10 引入、3.11 稳定的类型检查工具,用于向类型检查器声明函数返回 true 时参数满足特定类型;它仅影响静态检查,不改变运行时行为,且要求函数无副作用、返回类型明确标注为 typeguard[t],不支持联合类型或泛型参数。

什么是 TypeGuard,它和普通类型注解有什么区别
TypeGuard 是 Python 3.10 引入、在 3.11 中稳定可用的类型工具,用于告诉类型检查器(如 mypy、pyright):某个函数返回 True 时,其参数一定满足某种类型约束。它不是运行时断言,而是给静态类型检查器“打标签”的机制。
关键点在于:
-
TypeGuard只影响类型检查器行为,不改变运行时逻辑 - 它必须用在函数返回类型标注中,且函数体里不能有副作用(比如修改全局状态)
- 类型检查器看到
if is_str_list(x): ...,就会在if分支内把x当作list[str]处理
常见误区是把它当成 isinstance 的替代品——其实不是。它只是让类型检查器“相信”你做了正确判断。
如何写一个合法的 TypeGuard 函数
要让类型检查器识别并信任你的守卫逻辑,函数需同时满足三件事:
- 返回类型明确标注为
TypeGuard[T] - 函数只做类型判定,不抛异常、不改输入、不打印日志
- 运行时逻辑必须和类型声明一致(否则检查器信了,但运行时报错)
例如,验证一个值是否为非空字符串列表:
from typing import TypeGuard, List <p>def is_nonempty_str_list(obj: object) -> TypeGuard[List[str]]: if not isinstance(obj, list): return False if len(obj) == 0: return False return all(isinstance(item, str) for item in obj)</p>
注意:
-
obj: object是推荐写法,避免类型检查器提前推断出具体类型而绕过守卫 - 不要用
def is_nonempty_str_list(obj) -> TypeGuard[List[str]]:(缺类型注解),mypy 会忽略它 -
all(...)是安全的,但若用any()或嵌套循环+提前 return,需确保所有False路径都覆盖完整类型不匹配情况
为什么 mypy 报错 “TypeGuard cannot be used with Union types”
当你写成这样,mypy 会直接报错:
def is_int_or_str(obj: object) -> TypeGuard[int | str]: # ❌ mypy 1.8+ 拒绝
return isinstance(obj, (int, str))
原因:
-
TypeGuard要求目标类型是“单一可判别类型”,int | str是联合类型,类型检查器无法在分支中精确缩小为其中某一个 - 如果真需要多类型守卫,得拆成两个独立函数:
is_int和is_str,再用elif链处理
正确做法示例:
def is_int(obj: object) -> TypeGuard[int]:
return isinstance(obj, int)
<p>def is_str(obj: object) -> TypeGuard[str]:
return isinstance(obj, str)</p><h1>使用时:</h1><p>if is_int(x):
reveal_type(x) # int
elif is_str(x):
reveal_type(x) # str</p>
另外,TypeGuard 不支持泛型参数绑定(如 TypeGuard[dict[K, V]]),目前只能用于具体化类型。
在真实项目中怎么验证 TypeGuard 是否生效
光写对函数没用,得确认类型检查器真按你预期工作。最直接方式是配合 reveal_type + mypy:
- 在守卫后插入
reveal_type(x),看输出是否为你标注的类型 - 用 pyright 的
python -m pyright --verbose查看类型推导日志 - 在 VS Code 中把鼠标悬停在变量上,看提示类型是否变化
一个小技巧:故意写个错误守卫,观察 mypy 是否报“unreachable code”:
def is_bool(obj: object) -> TypeGuard[bool]:
return False # ❌ 总返回 False
<p>if is_bool(x):
x.upper() # mypy 会标这里 unreachable —— 说明守卫被识别了</p>
容易被忽略的是:某些 IDE(如旧版 PyCharm)对 TypeGuard 支持不完整,即使 mypy 正常,编辑器也可能不更新悬停类型。建议以 mypy 输出为准,别全信代码补全提示。
类型守卫真正起作用的地方,往往在复杂嵌套结构解析或动态数据转换场景里——比如从 JSON dict 中提取字段并保证类型安全。这时候守卫函数本身要足够轻量,且逻辑必须 100% 可静态推理,不然类型检查器会放弃信任。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











