简体   繁体   English

python 类型注释中的链式引用

[英]Chained references in python type annotations

Say I have a function that takes a value and a arbitrary number of functions, let's call the function for chain_call.假设我有一个函数接受一个值和任意数量的函数,让我们调用 chain_call 的函数。

Without types a simple naive implementation would be:如果没有类型,一个简单的实现将是:

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

As you imagine, input_value could be anything really but it's always the same as the first and only required argument of the first Callable in *args: List[Callable] .正如您想象的那样, input_value 可以是任何东西,但它始终与*args: List[Callable]第一个Callable的第一个也是唯一必需的参数相同。

From here and forward the Callable ´s first and only required argument is the same type as the previous items return-type.从这里开始并转发Callable的第一个也是唯一必需的参数与前面的项目返回类型相同。

So far I've managed to define a quite generic type but it's too loose.到目前为止,我已经设法定义了一个非常通用的类型,但它太松散了。

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

What I'd really like is something like我真正喜欢的是这样的

T = TypeVar('T')

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

Where T for Callable n+1 is tr of Callable n and the final return-type is tr of Callable n_max .其中TCallable n+1trCallable n和最终返回类型是TR Callable n_max I'm not sure how to express this with the type system and would love any guidance.我不确定如何用类型系统表达这一点,并且希望得到任何指导。

Unfortunately, this is currently not something that's possible to type using PEP 484 type hints.不幸的是,目前无法使用 PEP 484 类型提示进行输入。

The best you can do is use overloads to approximate a signature: basically, we hard-code what the signature ought to be up to a certain number then fall back to inferring 'Any':你能做的最好的事情是使用重载来近似一个签名:基本上,我们对签名应该达到某个数字的内容进行硬编码,然后回退到推断“任何”:

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

Here, I hard-coded what ought to happen up to 3 input functions (and started with an overload for the special case where all the callables happen to have the same input and output type).在这里,我对最多 3 个输入函数应该发生的事情进行了硬编码(并从所有可调用函数碰巧具有相同输入和输出类型的特殊情况的重载开始)。

This technique is how typeshed currently types things like the zip function, which can accept an arbitrary number of iterables.这种技术是 typeshed 当前如何输入诸如zip函数之类的东西,它可以接受任意数量的可迭代对象。

Note: you may need to use the latest version of mypy from master for this code to work verbatim.注意:您可能需要使用来自 master 的最新版本的 mypy 才能使此代码逐字运行。

This fully typed function exists in dry-python/returns .这个完全类型化的函数存在于dry-python/returns

We call it flow :我们称之为flow

from returns.pipeline import flow

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

The thing is that flow is fully typed via a custom mypy plugin we ship with our library.问题是flow是通过我们的库mypy的自定义mypy插件完全输入的。 So, it will catch this error case (and many others ):因此,它将捕获此错误情况(以及许多其他情况):

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"

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

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

Plugin: https://github.com/dry-python/returns/blob/0f7d02d0c491a7c65c74e6c0645f12fccc53fe18/returns/contrib/mypy/_features/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