python 3键入varadic"apply"样式定义

Sam*_*son 4 python types typing python-3.x python-typing

我一直在努力编写"varadic"参数列表类型定义.

例如,给出类型:

def foo(fn, *args):
    return fn(*args)
Run Code Online (Sandbox Code Playgroud)

我能做的最好的就是使用这里的建议:

from typing import overload, Callable, TypeVar

A = TypeVar('A')
B = TypeVar('B')
C = TypeVar('C')
R = TypeVar('R')

@overload
def foo(fn: Callable[[A], R], a: A) -> R: ...
@overload
def foo(fn: Callable[[A, B], R], a: A, b: B) -> R: ...
@overload
def foo(fn: Callable[[A, B, C], R], a: A, b: B, c: C) -> R: ...

def foo(fn, *args):
    return fn(*args)
Run Code Online (Sandbox Code Playgroud)

这主要是做正确的事情...例如,给定:

def bar(i: int, j: int) -> None:
    print(i)
Run Code Online (Sandbox Code Playgroud)

以下成功:

foo(bar, 10, 12)
Run Code Online (Sandbox Code Playgroud)

虽然这些失败了:

foo(bar, 10)
foo(bar, 10, 'a')
foo(bar, 10, 12) + 1
Run Code Online (Sandbox Code Playgroud)

但如果我检查,mypy --strict我得到:

test.py:15: error: Function is missing a type annotation
Run Code Online (Sandbox Code Playgroud)

(这就是说最终foo定义本身没有任何类型)

我可以重新定义foo为:

def foo(fn: Callable[..., R], *args: Any) -> R:
    return fn(*args)
Run Code Online (Sandbox Code Playgroud)

但是当我跑步时,mypy --strict我得到:

test.py:15: error: Overloaded function implementation does not accept all possible arguments of signature 1
test.py:15: error: Overloaded function implementation does not accept all possible arguments of signature 2
test.py:15: error: Overloaded function implementation does not accept all possible arguments of signature 3
Run Code Online (Sandbox Code Playgroud)

我真的不明白.

如果有人可以建议一种更好的方式给这种功能类型,将不胜感激!如果我也可以这样做而不列出很多overload很好的s,那么真正的定义也会有一些"仅限关键字"的参数,这些参数最好不要每次都重复

Mic*_*x2a 5

你得到"重载函数实现不接受所有可能的参数......"错误的原因是因为你的重载实现没有正确处理看起来像这样的调用:foo(my_callable, a=3, b=4).

毕竟,根据您的过载签名,用户理论上可以明确地使用a,b,c等命名参数 - 因此,您的实现需要支持这些类型的调用.

有两种不同的方法可以解决这个问题.

第一种方法是**kwargs: Any修改并修改您的重载实现,如下所示:

def foo(fn: Callable[..., R], *args: Any, **kwargs: Any) -> Any:
    return fn(*args, **kwargs)
Run Code Online (Sandbox Code Playgroud)

现在您的实现将正确处理这些类型的调用.

第二种方法是为每个参数添加两个下划线,如下所示:

@overload
def foo(fn: Callable[[A], R], __a: A) -> R: ...
@overload
def foo(fn: Callable[[A, B], R], __a: A, __b: B) -> R: ...
@overload
def foo(fn: Callable[[A, B, C], R], __a: A, __b: B, __c: C) -> R: ...

def foo(fn: Callable[..., R], *args: Any) -> Any:
    return fn(*args)
Run Code Online (Sandbox Code Playgroud)

当mypy看到以两个下划线开头的参数时,它理解该参数仅仅是位置的.所以,mypy会拒绝像这样的电话foo(my_fn, __a=3, __b=4).

这是一个打字的东西.使用两个下划线为参数添加前缀在运行时没有特殊含义.


关于你不必重复这么多重载的更广泛的问题:不幸的是,在一堆重载上加上我们现在能做的最好.您正在使用的技术是使用typeshed键入类似功能相同的技术map(...)filter(...),例如.

为了做得更好,我们需要一个名为variadic泛型的功能- 但它们是一个复杂的功能,但遗憾的是mypy还不支持它们.希望计划在2019年晚些时候实现它们,所以你可能会淘汰重载.