繁体   English   中英

python 类型注释中的链式引用

[英]Chained references in python type annotations

假设我有一个函数接受一个值和任意数量的函数,让我们调用 chain_call 的函数。

如果没有类型,一个简单的实现将是:

def chain_call(input_value, *args):
    for function in args:
        input_value = function(input_value)
    return input_value

正如您想象的那样, input_value 可以是任何东西,但它始终与*args: List[Callable]第一个Callable的第一个也是唯一必需的参数相同。

从这里开始并转发Callable的第一个也是唯一必需的参数与前面的项目返回类型相同。

到目前为止,我已经设法定义了一个非常通用的类型,但它太松散了。

def chain_call(input_value: Any, *args: List[Callable[Any], Any]) -> Any: ...

我真正喜欢的是这样的

T = TypeVar('T')

def chain_call(input_value: T, *args: List[Callable[T, ...], tr]) -> tr: ...

其中TCallable n+1trCallable n和最终返回类型是TR Callable n_max 我不确定如何用类型系统表达这一点,并且希望得到任何指导。

不幸的是,目前无法使用 PEP 484 类型提示进行输入。

你能做的最好的事情是使用重载来近似一个签名:基本上,我们对签名应该达到某个数字的内容进行硬编码,然后回退到推断“任何”:

from typing import TypeVar, overload, Any, Callable

T1 = TypeVar('T1')
T2 = TypeVar('T2')
T3 = TypeVar('T3')
T4 = TypeVar('T4')

@overload
def chain_call(input_value: T1, 
               *f_rest: Callable[[T1], T1]) -> T1: ...
@overload
def chain_call(input_value: T1, 
               f1: Callable[[T1], T2],
               f2: Callable[[T2], T3],
               f3: Callable[[T3], T4],
               f4: Callable[[T4], Any],
               *f_rest: Callable[[Any], Any]) -> Any: ...
@overload
def chain_call(input_value: T1, 
               f1: Callable[[T1], T2],
               f2: Callable[[T2], T3],
               f3: Callable[[T3], T4]) -> T4: ...
@overload
def chain_call(input_value: T1, 
               f1: Callable[[T1], T2],
               f2: Callable[[T2], T3]) -> T3: ...
@overload
def chain_call(input_value: T1, 
               f1: Callable[[T1], T2]) -> T2: ...
def chain_call(input_value, *f_rest):
    for function in f_rest:
        input_value = function(input_value)
    return input_value

在这里,我对最多 3 个输入函数应该发生的事情进行了硬编码(并从所有可调用函数碰巧具有相同输入和输出类型的特殊情况的重载开始)。

这种技术是 typeshed 当前如何输入诸如zip函数之类的东西,它可以接受任意数量的可迭代对象。

注意:您可能需要使用来自 master 的最新版本的 mypy 才能使此代码逐字运行。

这个完全类型化的函数存在于dry-python/returns

我们称之为flow

from returns.pipeline import flow

assert flow('1', int, float, str) == '1.0'

问题是flow是通过我们的库mypy的自定义mypy插件完全输入的。 因此,它将捕获此错误情况(以及许多其他情况):

from returns.pipeline import flow

def convert(arg: str) -> float:
    ...

flow('1', int, convert)
# error: Argument 1 to "convert" has incompatible type "int"; expected "str"

文档: https : //returns.readthedocs.io/en/latest/pages/pipeline.html

来源: https : //github.com/dry-python/returns/blob/0f7d02d0c491a7c65c74e6c0645f12fccc53fe18/returns/_internal/pipeline/flow.py

插件: https : //github.com/dry-python/returns/blob/0f7d02d0c491a7c65c74e6c0645f12fccc53fe18/returns/contrib/mypy/_features/flow.py

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM