如何在Python运行时强制校验组合(参数化泛型)类型提示?
Python中强制校验参数化泛型类型提示的实现方案
可以实现对list[int]这类参数化泛型的类型校验,下面是几种可行的解决方案:
方案1:使用beartype库(推荐,轻量原生支持)
beartype是专注于运行时类型校验的库,原生支持参数化泛型的校验逻辑,用法简洁直接:
from beartype import beartype @beartype def my_fn(a: list[int]) -> None: pass my_fn(a=[1, 2]) # 正常执行 my_fn(a=[1, '2']) # 抛出BeartypeCallHintParamViolation异常,符合预期
方案2:使用pydantic的参数校验装饰器
如果项目已经在用pydantic做数据验证,直接用它的validate_arguments装饰器即可实现泛型校验:
from pydantic import validate_arguments @validate_arguments def my_fn(a: list[int]) -> None: pass my_fn(a=[1, 2]) # 正常执行 my_fn(a=[1, '2']) # 抛出ValidationError异常,明确提示元素类型错误
方案3:手动实现泛型校验(无第三方依赖)
如果不想引入第三方库,可以基于Python的typing模块解析类型提示,自己实现校验逻辑:
针对list[int]的简易版本
def enforce_list_int(func): def wrapper(*args, **kwargs): a = kwargs.get('a', args[0] if args else None) # 先校验外层list类型 if not isinstance(a, list): raise TypeError(f"参数a必须是list类型,实际为{type(a)}") # 校验每个元素的int类型 for idx, item in enumerate(a): if not isinstance(item, int): raise TypeError(f"列表a第{idx}个元素类型错误:预期int,实际为{type(item)}") return func(*args, **kwargs) return wrapper @enforce_list_int def my_fn(a: list[int]) -> None: pass my_fn(a=[1, 2]) # 正常执行 my_fn(a=[1, '2']) # 抛出TypeError,符合预期
通用泛型校验版本(支持更多泛型类型)
如果需要支持dict[str, int]、tuple[int, str]等更多泛型,可以用typing.get_origin和typing.get_args解析类型提示:
import inspect from typing import get_args, get_origin def enforce_generic_types(func): sig = inspect.signature(func) param_annotations = {name: param.annotation for name, param in sig.parameters.items()} def wrapper(*args, **kwargs): bound_args = sig.bind(*args, **kwargs).arguments for param_name, param_value in bound_args.items(): anno = param_annotations[param_name] origin_type = get_origin(anno) # 判断是否是参数化泛型 if origin_type is None: continue # 处理list类型 if origin_type is list: if not isinstance(param_value, list): raise TypeError(f"参数{param_name}必须是{anno}类型,实际为{type(param_value)}") inner_type = get_args(anno)[0] for idx, item in enumerate(param_value): if not isinstance(item, inner_type): raise TypeError(f"{param_name}第{idx}个元素类型错误:预期{inner_type},实际为{type(item)}") # 扩展处理dict类型 elif origin_type is dict: key_type, val_type = get_args(anno) if not isinstance(param_value, dict): raise TypeError(f"参数{param_name}必须是{anno}类型,实际为{type(param_value)}") for k, v in param_value.items(): if not isinstance(k, key_type) or not isinstance(v, val_type): raise TypeError(f"{param_name}中键值对({k}:{v})类型错误:预期键{key_type}、值{val_type}") return func(*args, **kwargs) return wrapper @enforce_generic_types def my_fn(a: list[int]) -> None: pass @enforce_generic_types def my_dict_fn(b: dict[str, int]) -> None: pass my_fn(a=[1, 2]) # 正常执行 my_fn(a=[1, '2']) # 抛出TypeError my_dict_fn(b={"age": 20}) # 正常执行 my_dict_fn(b={"age": "20"}) # 抛出TypeError
补充说明
你之前尝试的type_enforced库对参数化泛型的支持有限,因此无法实现预期效果,上面的三种方案都能满足你的需求,其中beartype和pydantic是成熟的第三方库,推荐优先使用。
内容的提问来源于stack exchange,提问作者user64150
相关产品推荐
相关产品推荐

