Python/mypy中判断泛型类型子类型关系的简便及交互式方法
Python泛型子类型关系的交互式判断方法
Python内置的issubclass()无法直接判断参数化泛型的子类型关系,因为参数化泛型(如list[bool])不属于合法的issubclass()第二个参数类型,会触发TypeError。我们可以通过解析泛型的原始类、类型参数以及泛型的变异性(协变/逆变/不变)来实现自定义判断逻辑。
基础实现(支持协变场景)
先实现一个基础版本,处理单参数/多参数泛型的子类型判断,默认适配list这类容器的协变行为:
from typing import get_origin, get_args def is_generic_subclass(sub, sup): # 获取泛型的原始类(如list[bool]的原始类是list) sub_origin = get_origin(sub) sup_origin = get_origin(sup) # 处理非参数化泛型的常规判断 if sub_origin is None or sup_origin is None: try: return issubclass(sub, sup) except TypeError: return False # 泛型本身必须是子类关系(如list是Sequence的子类) if not issubclass(sub_origin, sup_origin): return False sub_args = get_args(sub) sup_args = get_args(sup) if len(sub_args) != len(sup_args): return False # 逐个检查类型参数的子类型关系(协变逻辑:子泛型的参数需是父泛型参数的子类) for sub_arg, sup_arg in zip(sub_args, sup_args): if not issubclass(sub_arg, sup_arg): return False return True
测试示例:
# 基础容器泛型测试 print(is_generic_subclass(list[bool], list[int])) # 输出True,因为bool是int的子类 print(is_generic_subclass(list[int], list[bool])) # 输出False print(is_generic_subclass(dict[str, int], dict[object, object])) # 输出True,str、int均为object子类
进阶实现(支持协变/逆变/不变)
如果需要处理自定义泛型的变异性(如逆变的Callable),可以进一步解析泛型类的TypeVar变异性,调整判断逻辑:
from typing import get_origin, get_args, Generic, TypeVar def is_generic_subclass(sub, sup): sub_origin = get_origin(sub) sup_origin = get_origin(sup) if sub_origin is None or sup_origin is None: try: return issubclass(sub, sup) except TypeError: return False if not issubclass(sub_origin, sup_origin): return False sub_args = get_args(sub) sup_args = get_args(sup) if len(sub_args) != len(sup_args): return False # 获取泛型类定义的TypeVar及其变异性 type_vars = sub_origin.__parameters__ for idx, (sub_arg, sup_arg) in enumerate(zip(sub_args, sup_args)): tv = type_vars[idx] if tv.__covariant__: # 协变:子参数需是父参数的子类 if not issubclass(sub_arg, sup_arg): return False elif tv.__contravariant__: # 逆变:父参数需是子参数的子类(如Callable[[int], None]是Callable[[bool], None]的子类) if not issubclass(sup_arg, sub_arg): return False else: # 不变:类型参数必须完全一致 if sub_arg is not sup_arg: return False return True
测试逆变场景:
# 定义逆变泛型类 T_contra = TypeVar('T_contra', contravariant=True) class MyContravariant(Generic[T_contra]): pass # 逆变逻辑下,MyContravariant[int]是MyContravariant[bool]的子类 print(is_generic_subclass(MyContravariant[int], MyContravariant[bool])) # 输出True print(is_generic_subclass(MyContravariant[bool], MyContravariant[int])) # 输出False
你可以直接调用上述is_generic_subclass()函数,交互式地判断任意泛型的子类型关系,无需担心issubclass()的报错问题。
内容的提问来源于stack exchange,提问作者geckos
相关产品推荐
相关产品推荐

