Python 中类型友好的委托

lin*_*k89 9 python python-3.x python-typing

考虑以下代码示例

def sum(a: int, b: int):
  return a + b

def wrap(*args, **kwargs):
  # delegate to sum
  return sum(*args, **kwargs)
Run Code Online (Sandbox Code Playgroud)

除了类型提示丢失之外,代码运行良好。*args, **kwargs在 Python 中用来实现委托模式是很常见的。如果有一种方法可以在使用它们时保留类型提示,那就太好了,但我不知道是否可能以及如何实现。

Ant*_*tis 4

有关此问题的详细讨论,请参阅https://github.com/python/typing/issues/270 。您可以通过wrap使用适当类型的标识函数进行装饰来实现此目的:

F = TypeVar("F", bound=Callable)
def copy_signature(_: F) -> Callable[..., F]:
    return lambda f: f

def s(x: int, y: int) -> int:
    return x + y

@copy_signature(s)
def wrap(*args, **kwargs):
    s(*args, **kwargs)

reveal_type(wrap)  # Revealed type is "def (x: int, y: int) -> int"
Run Code Online (Sandbox Code Playgroud)

据我所知,装饰器是必要的 - 即使使用 PEP612,单独使用类型提示仍然不可能做到这一点。由于在这种情况下使用functools.wraps装饰器(复制运行类型信息)已经是很好的实践,所以这并不是什么损失 - 您可以改为定义

def wraps(f: F) -> Callable[..., F]:
    return functools.wraps(f) # type: ignore
Run Code Online (Sandbox Code Playgroud)

然后,只要您使用此装饰器,运行时和静态类型信息都应该是正确的。(遗憾的是,mypy 中包含的 typeshed 存根functools.wraps并没有足够的限制,无法开箱即用。)

PEP612 添加了在包装器中添加/删除参数的功能(通过与 结合ParamSpecConcatenate,但它并没有消除对某种高阶函数(如装饰器)的需要,以让类型系统wrap从中推断出 的签名的s