Python 3:为参数类型转换装饰器添加正确的类型提示
解决方案
要实现保留原函数参数数量、将所有参数类型改为Any且保留原返回类型的装饰器类型提示,需要结合ParamSpec和对应Python版本的类型扩展语法。
代码实现(Python 3.12+)
from typing import Callable, ParamSpec, TypeVar, Any import inspect P = ParamSpec("P") R = TypeVar("R") def coerce_args(func: Callable[P, R]) -> Callable[P[Any, ...], R]: """将函数参数强制转换为其类型提示指定的类型""" sig = inspect.signature(func) param_types = { name: param.annotation for name, param in sig.parameters.items() if param.annotation is not inspect.Parameter.empty } def wrapper(*args: Any, **kwargs: Any) -> R: # 绑定参数并应用默认值 bound_args = sig.bind(*args, **kwargs) bound_args.apply_defaults() # 转换参数类型 for name, value in bound_args.arguments.items(): if name in param_types: bound_args.arguments[name] = param_types[name](value) return func(*bound_args.args, **bound_args.kwargs) return wrapper # 测试示例 @coerce_args def test(x: int) -> int: return x * 2 # 类型检查器会提示 test 的签名为 (x: Any) -> int print(test("5")) # 输出 10,运行时转换为int print(test(3.14)) # 输出 6,运行时转换为int # test() 会触发类型检查错误(缺少参数x),符合需求
兼容Python 3.10/3.11的写法(使用typing_extensions)
如果使用Python 3.10或3.11,需要先安装typing_extensions库,再通过类型转换间接实现需求:
from typing_extensions import ParamSpec, TypeVar, Callable, Any import inspect from typing import cast P = ParamSpec("P") R = TypeVar("R") def coerce_args(func: Callable[P, R]) -> Callable[P, R]: """将函数参数强制转换为其类型提示指定的类型""" sig = inspect.signature(func) param_types = { name: param.annotation for name, param in sig.parameters.items() if param.annotation is not inspect.Parameter.empty } def wrapper(*args: Any, **kwargs: Any) -> R: bound_args = sig.bind(*args, **kwargs) bound_args.apply_defaults() for name, value in bound_args.arguments.items(): if name in param_types: bound_args.arguments[name] = param_types[name](value) return func(*bound_args.args, **bound_args.kwargs) # 告知类型检查器:wrapper保留原参数结构,但允许传入任意类型 return cast(Callable[P, R], wrapper)
关键说明
ParamSpec[P]用于捕获原函数的完整参数结构(包括参数数量、名称、位置/关键字属性)。- Python 3.12+的
P[Any, ...]语法可以直接将P中所有参数的类型替换为Any,同时严格保留原参数结构。 - 旧版本通过
cast结合Callable[P, R],让类型检查器维持原参数数量要求,同时运行时接受任意类型参数,间接达成需求。
内容的提问来源于stack exchange,提问作者Shane Breeze
相关产品推荐
相关产品推荐

