python 3 typing varadic "apply"样式定义

标签 python python-3.x types type-hinting python-typing

我一直在努力写"varadic" argument lists类型定义。

例如,将类型赋予:

def foo(fn, *args):
    return fn(*args)

我能做的最好的就是使用 a suggestion from here :

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)

哪个主要做正确的事……例如,给定:

def bar(i: int, j: int) -> None:
    print(i)

以下成功:

foo(bar, 10, 12)

虽然这些都失败了:

foo(bar, 10)
foo(bar, 10, 'a')
foo(bar, 10, 12) + 1

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

test.py:15: error: Function is missing a type annotation

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

我可以重新定义 foo成为:

def foo(fn: Callable[..., R], *args: Any) -> R:
    return fn(*args)

但是当我运行 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

我不太明白。

如果有人可以建议一种更好的方法来为这种函数提供类型,我们将不胜感激!如果我也可以在不列出大量 overload 的情况下做到这一点那样就好了,真正的定义也有一些“仅关键字”参数,不用每次都重复就好了

最佳答案

您收到“重载函数实现不接受所有可能的参数...”错误的原因是因为您的重载实现没有正确处理如下所示的调用: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)

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

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

@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)

当 mypy 看到一个以两个下划线开头的参数时,它会理解该参数仅用于位置。因此,mypy 将拒绝像 foo(my_fn, __a=3, __b=4) 这样的调用。

不过,这是一个只能输入的东西。使用两个下划线作为参数前缀在运行时没有特殊意义。


关于您关于不必重复这么多过载的更广泛的问题:不幸的是,解决一堆过载是我们目前能做的最好的事情。例如,您使用的技术与 typeshed 用于键入 map(...)filter(...) 等函数的技术相同。

为了做得更好,我们需要一个称为可变泛型 的特性——但它们是一个复杂的特性,不幸的是,mypy 还不支持它们。不过,我们的计划是希望在 2019 年晚些时候实现它们,这样您就可以在那时消除过载。

关于python 3 typing varadic "apply"样式定义,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53947090/

相关文章:

Python:对象到str的隐式转换?

python - 过滤掉列表中稳定值的更多 pythonic 方法

python - 使用不区分大小写的文本查询 pymongo

python - 如何解释 Python 3.6 中的 str.maketrans 函数?

python - 计算年龄的程序给出关于 getset_descriptor 的错误?

c# - 从 CLR 样式的类型全名获取 C# 样式的类型引用

C++ 中的 Python 或纯 C++ 用于文件模板操作?

Python SciPy : optimization issue fmin_cobyla : one constraint is not respected

python - 如何根据环境从我的 settings.py DATABASES 数组中激活数据库设置?

haskell - 逆变位置上较高等级类型的统一