如何在不使用Any类型的情况下为tenacity重试包装函数添加类型标注?
如何在不使用Any类型的情况下为tenacity重试包装函数添加类型标注?
当然可以!ParamSpec正是解决这类问题的绝佳方案,它能让我们精准复用原函数的参数签名和返回值类型,彻底摆脱Any的模糊标注,让类型检查器准确识别所有约束。
先给你看改造后的代码,完全符合类型安全要求:
from typing import ParamSpec, TypeVar import tenacity # 定义参数规格和返回值类型变量,用来捕获原函数的类型信息 P = ParamSpec("P") R = TypeVar("R") # 假设这是你需要添加重试逻辑的原函数,先给它加上明确的类型标注 def foo(a: int, b: str) -> None: print(f"执行foo:a={a}, b={b}") @tenacity.retry def foo_with_retry(*args: P.args, **kwargs: P.kwargs) -> R: # 这里的P和R会自动匹配foo的参数和返回值类型 return foo(*args, **kwargs)
如果要做一个可复用的通用重试装饰器(能套在任意函数上),写法会更灵活:
from typing import ParamSpec, TypeVar, Callable import tenacity P = ParamSpec("P") R = TypeVar("R") def with_retry(func: Callable[P, R]) -> Callable[P, R]: @tenacity.retry def wrapper(*args: P.args, **kwargs: P.kwargs) -> R: return func(*args, **kwargs) return wrapper # 直接用这个装饰器包装任意带类型标注的函数 @with_retry def foo(a: int, b: str) -> None: print(f"执行foo:a={a}, b={b}") @with_retry def add(x: float, y: float) -> float: return x + y
简单说下原理
ParamSpec("P")会完整捕获被包装函数的参数签名(包括位置参数、关键字参数的具体类型)TypeVar("R")用来捕获被包装函数的返回值类型- 通过
Callable[P, R],我们把包装函数的类型和原函数严格绑定,像mypy、pyright这类静态类型检查工具,能准确识别参数约束,再也不用依赖模糊的Any类型。
这样改造后,不仅类型标注更严谨,还能让检查工具帮你提前发现参数传递错误——比如给foo_with_retry传字符串类型的a,检查器会直接报错提示。
内容来源于stack exchange
相关产品推荐
相关产品推荐

