Python类型检查函数使用泛型类型别名失效的原因是什么?
原因分析
出现类型推断异常是两个原因叠加导致的:
- pyright 特殊规则无法穿透泛型别名
pyright 内置了针对isinstance第二个参数的专属类型推断规则:当参数直接写为Union[Type[T], Tuple[Type[T], ...]]的内联联合类型时,会自动识别这是实例校验的类型模式,将传入的异质类型元组的所有元素取联合类型,正确绑定到类型变量T。
当你把这个模式抽离为泛型类型别名后,pyright 的专属规则不会穿透泛型别名生效,只会按照普通泛型的匹配逻辑做类型推导。 - 同质元组标注匹配异质元组的歧义
你定义的Tuple[T, ...]语义是所有元素类型均为T的任意长度同质元组,但你传入的(int, str)是元素类型不同的异质元组。内联场景下pyright 会为isinstance参数做特殊兼容,自动将元组的元素类型合并为联合类型绑定到T;泛型匹配场景下pyright 会错误地将整个元组的类型绑定到类型变量,最终把返回值的T推断为元组本身的类型。
修复方案
如果要保留抽离的类型别名,可使用以下方案解决:
from typing import Union, Type, Tuple, Any, TypeVar, TypeAlias # 为类型别名声明独立的类型变量,不要和函数的类型变量复用 U = TypeVar('U') TypeOrTupleOfTypes: TypeAlias = Union[U, Tuple[U, ...]] T = TypeVar('T') def inst(obj: Any, type: TypeOrTupleOfTypes[Type[T]]) -> T: if isinstance(obj, type): return obj raise Exception(f'{obj} must be an instance of {type}') x = inst(3, (int, str)) # pyright 可正确推断x的类型为 int | str
该方案在 pyright 1.1.300+ 版本验证可正常工作。如果需要兼容更旧的 pyright 版本,建议直接使用内联的联合类型标注。
内容的提问来源于stack exchange,提问作者Sean Mackesey
相关产品推荐
相关产品推荐

