如何为任意参数数量的函数调用器添加正确的类型注解
实现带类型检查的通用函数调用器
我需要实现一个函数调用器fun_caller,它能接收任意函数及其对应参数并执行,但之前的两种方案都有局限:
现有方案的问题
方案一:仅支持固定参数数量
这个方案能让类型检查器识别参数的数量和类型是否正确,但只能处理恰好2个参数的函数,没法通用到任意参数数量的场景:
from typing import TypeVar from collections.abc import Callable T0 = TypeVar("T0") T1 = TypeVar("T1") T2 = TypeVar("T2") def my_fun(a: str, b: int) -> None: print(int(a) + b) return None def fun_caller(f: Callable[[T0, T1], T2], *args: tuple[T0, T1]) -> T2: return f(*args) fun_caller(my_fun, "1", 2)
方案二:支持任意参数但无类型检查
这个方案能处理任意参数数量的函数,但类型检查器没法检测参数数量、类型错误,只能等运行时抛出TypeError:
from typing import TypeVar, Any from collections.abc import Callable T = TypeVar("T") def my_fun(a: str, b: int) -> None: print(int(a) + b) return None def fun_caller(f: Callable[..., T], *args: tuple[Any, ...]) -> T: return f(*args) # 这里期望类型检查器报错:第一个参数应为str,且只需要2个参数而非3个,但实际不会 fun_caller(my_fun, 0, 2, "a")
通用解决方案
使用Python 3.10+引入的ParamSpec,结合TypeVar可以实现既支持任意参数数量和类型,又能让类型检查器正确校验的fun_caller:
from typing import TypeVar, ParamSpec from collections.abc import Callable # P用于捕获目标函数的参数签名,T用于捕获返回值类型 P = ParamSpec("P") T = TypeVar("T") def my_fun(a: str, b: int) -> None: print(int(a) + b) def fun_caller(f: Callable[P, T], *args: P.args, **kwargs: P.kwargs) -> T: return f(*args, **kwargs) # 正确调用:类型检查通过 fun_caller(my_fun, "1", 2) # 以下错误调用会被类型检查器直接识别: # 错误1:参数数量过多 # fun_caller(my_fun, "1", 2, "extra") # 错误2:第一个参数类型不匹配(应为str,传入了int) # fun_caller(my_fun, 0, 2)
方案说明
ParamSpec("P")会完整捕获目标函数的所有参数信息,包括位置参数、关键字参数的类型和数量Callable[P, T]约束传入的函数必须符合P定义的参数签名,返回值类型为T*args: P.args和**kwargs: P.kwargs确保传入fun_caller的参数和目标函数的参数严格匹配- 主流类型检查器(如mypy、Pyright)会在静态检查阶段就识别出参数数量、类型不匹配的问题,不用等到运行时
内容的提问来源于stack exchange,提问作者Carlos
相关产品推荐
相关产品推荐

