mypy:如何在可调用类型上应用 *args?

Sel*_*lva 0 python mypy python-typing

我有一个如下所示的功能,

def run_the_f(f):
  # run f function after some validation
Run Code Online (Sandbox Code Playgroud)

根据某些条件,f 函数的签名会发生如下变化f(1.0), f(1.0,2.0), f(1.0,2.0,3.0,..)。换句话说,f 中输入参数的数量可以变化,类似于pyspark 中的 udf f

我正在使用 mypy 并且我在下面尝试了失败,

def run_the_f(f: Callable[[*float],float]):
  # run f after some validation
Run Code Online (Sandbox Code Playgroud)

有人可以支持在 Callable 中填写什么吗?

Mec*_*Pig 5

使用typing.Protocol

from typing import Protocol, TypeVar
from abc import abstractmethod

T_contra = TypeVar('T_contra', contravariant=True)
T_co = TypeVar('T_co', covariant=True)


class SupportsCallWithArgs(Protocol[T_contra, T_co]):

    @abstractmethod
    def __call__(self, *args: T_contra) -> T_co:
        pass


def run_the_f(f: SupportsCallWithArgs[float, float]):
    f(1., 2., 3.)   # pass
    f([])           # fail: Argument to "__call__" of "SupportsCallWithArgs" has incompatible type "List[<nothing>]";
                    # expected "float"

def f(*args: float) -> float:
    pass


run_the_f(f)             # pass
run_the_f(lambda: None)  # fail: Argument 1 to "run_the_f" has incompatible type "Callable[[], None]";
                         # expected "SupportsCallWithArgs[float, float]"
Run Code Online (Sandbox Code Playgroud)

VarArg在mypy的扩展包中也许是一个更简单的选择(根据评论区提供的参考,不推荐这种选择):

from collections.abc import Callable
from mypy_extensions import VarArg


def run_the_f(f: Callable[[VarArg(float)], float]):
    f(1., 2., 3.)    # pass
Run Code Online (Sandbox Code Playgroud)

但这似乎只是mypy的自我催眠,Pycharm的类型检查器会提示2.and3.作为意外的参数f

# mypy_extensions.py
def VarArg(type=Any):
    """A *args-style variadic positional argument"""
    return type
Run Code Online (Sandbox Code Playgroud)