在 Python 中键入一个可变参数函数

Typehint a variadic function in Python

我写了一个高阶python函数(姑且称之为parent),它的参数函数(姑且称之为child)是一个可变参数函数。

我不知道如何输入提示。

child 将始终是 str 的第一个参数和可变数量的参数作为参数,可以是任何参数。它returns Any.

我能得到的最接近它的是 Callable[..., Any] 但后来我“失去”了第一个参数是 str.

的事实

我想要这样的东西 Callable[[str,...], Any] 但这不是有效的类型提示。

有没有办法对我的函数进行类型提示?

使用 Protocol 不需要您手动将值包装在“类型提示包装器”中,但不幸的是它在这里对您没有帮助。

如果一个函数可以匹配一个协议,它的签名必须与协议中__call__方法的签名完全匹配。但是,(如果我没记错的话)你想匹配任何函数与一个字符串作为第一个参数,它可以是以下任何一个:

def fn1(x: str) -> Any: ...
def fn2(x: str, arg1: int, arg2: float) -> Any: ...
def fn3(x: str, *args: Any, **kwargs: Any) -> Any: ...

这些都有不同的签名,因此无法通过单一协议匹配:(mypy-play)

from typing import Any, Protocol

# This might be the protocol you might use, but unfortunately it doesn't work.
class StrCallable(Protocol):
  def __call__(self, x: str, *args, **kwargs) -> Any:
    ...

def my_higher_order_fn(fn: StrCallable) -> StrCallable:
  return fn  # fill this with your actual implementation

my_higher_order_fn(fn1)  # fails
my_higher_order_fn(fn2)  # fails
my_higher_order_fn(fn3)  # this passes, though

PEP 612 引入了 ParamSpec,这是您在这里需要的。它有点像 TypeVar,但用于函数签名。您可以在 Callable:

中放置第一个列表参数的地方放置 ParamSpec
from typing import Callable, Concatenate, ParamSpec, TypeVar

P = ParamSpec("P")
TRet = TypeVar("TRet")

StrCallable = Callable[Concatenate[str, P], TRet]

其中 Concatenate 将类型连接到现有参数规范。 Concatenate[str, P] 正是您所需要的:第一个参数为 str.

的任何函数签名

不幸的是,PEP 612 直到 Python 3.10 才可用,而且 mypy 还没有完全支持它。在那之前,您可能只需要使用 Callable[..., TRet].