如何检查函数实参类型是否与typing模块指定的类型匹配?
实现类型提示验证装饰器
checktypes 下面是满足需求的装饰器实现,支持标准类型和typing模块中的抽象类型(如Iterable),不匹配时会输出清晰的错误信息:
import inspect import typing from typing import get_origin, get_args def checktypes(func): # 获取函数签名和类型提示 func_sig = inspect.signature(func) type_hints = typing.get_type_hints(func) def wrapper(*args, **kwargs): # 绑定传入的实参到函数签名,包含默认参数 bound_args = func_sig.bind(*args, **kwargs) bound_args.apply_defaults() for param_name, param_value in bound_args.arguments.items(): # 跳过没有类型提示的参数 if param_name not in type_hints: continue expected_type = type_hints[param_name] # 验证参数类型 is_valid = False # 处理泛型类型(如Iterable、List等) origin_type = get_origin(expected_type) if origin_type is not None: is_valid = isinstance(param_value, origin_type) else: # 处理标准类型(如int、str) is_valid = isinstance(param_value, expected_type) if not is_valid: # 构造格式化的错误信息 func_name = func.__name__ # 简化类型名称,去掉typing.前缀 expected_type_str = str(expected_type).replace("typing.", "") actual_type = type(param_value).__name__ raise TypeError( f"'{param_name}' to '{func_name}()' must be of type '{expected_type_str}', not type '{actual_type}'" ) from None return func(*args, **kwargs) return wrapper
测试示例
使用你提供的测试代码验证效果:
from typing import Iterable @checktypes def some_function(arg1: str, arg2: int, arg3: Iterable) -> None: pass # 传入不符合类型的参数 some_function("string", 1, True)
执行后会输出预期的错误:
TypeError: 'arg3' to 'some_function()' must be of type 'Iterable', not type 'bool'
关键细节说明
- 参数绑定:用
inspect.signature.bind处理位置参数和关键字参数,确保能覆盖所有传入的参数(包括带默认值的参数) - 类型提示解析:
typing.get_type_hints比直接读取__annotations__更可靠,会自动解析字符串形式的类型提示(Python 3.9+支持) - 泛型类型处理:通过
get_origin获取泛型类型的原始抽象类型(比如typing.Iterable的原始类型是collections.abc.Iterable),确保isinstance判断准确 - 错误信息优化:去掉类型名称中的
typing.前缀,让错误信息更简洁易读
兼容与扩展说明
- 对于Python 3.8及以下版本,
get_origin和get_args需要从typing_extensions库导入,可通过以下方式兼容:try: from typing import get_origin, get_args except ImportError: from typing_extensions import get_origin, get_args - 当前实现仅验证参数的顶层类型,如果需要验证泛型的元素类型(如
List[int]中的int),可以通过get_args获取元素类型后遍历验证 - 支持联合类型(如
Union[int, str]),因为isinstance原生支持传入多个类型进行判断
内容的提问来源于stack exchange,提问作者darragh
相关产品推荐
相关产品推荐

