如何使用类型提示在生成器上注释装饰器?

时间:2019-02-05 06:10:54

标签: python python-3.x generator type-hinting

我正在使用David Beazley(在http://www.dabeaz.com/coroutines/(在{{3}}的精彩的三元组演示文稿中所描述的)作为协程生成器,我不知道如何键入装饰器consumer。这是我到目前为止的内容:

from typing import Any, Callable, Generator, Iterable

ArbGenerator = Generator[Any, Any, Any]

def consumer(fn: ❓) -> ❓:
    @wraps(fn)
    def start(*args: Any) -> ArbGenerator:
        c = fn(*args)
        c.send(None)
        return c
return start

使用示例,缩写为

@consumer
def identity(target: ArbGenerator) -> ArbGenerator:
    while True:
        item = yield
        target.send(item)

@consumer
def logeach(label: Any, target: ArbGenerator) -> ArbGenerator:
    while True:
        item = yield
        print(label, item)
        target.send(item)

pipeline = identity(logeach("EXAMPLE", some_coroutine_sink()))

粗体标记了我不确定的地方-我也不确定我定义的类型ArbGenerator。 (问题是,在没有键入装饰器函数consumer本身的情况下,我不确定mypy是否正在使用该装饰器分析任何生成器函数,因此这就是为什么我不确定ArbGenerator的原因。 )

我对最紧密的类型感兴趣,它比Any更好,因此当我编写这些协程的链时,mypy如果链条设置不正确,将会给我很好的警告。

(如果需要,请使用Python 3.5。)

1 个答案:

答案 0 :(得分:1)

作为一种更具体的方法,您可以执行以下操作:

  1. 使用Callable类型而不是问号。

  2. typing.Coroutine用于targets,并放下ArbGenerator

  3. 协程返回一个生成器,返回类型可以是Generator或它的超类型之一

您应该使用可调用而不是问号的原因是fn首先应该是可调用对象,这就是为什么用装饰器将其包装的原因。 Coroutine将在调用对象后创建,并且返回类型显然也应该是可调用对象。

from typing import Any, Callable,Generator, Coroutine
from functools import wraps


def consumer(fn: Callable) -> Callable:
    @wraps(fn)
    def start(*args: Any) -> Coroutine:
        c = fn(*args)  # type: Coroutine
        c.send(None)
        return c
    return start


@consumer
def identity(target: Coroutine) -> Generator:
    while True:
        item = yield
        target.send(item)

@consumer
def logeach(label: Any, target: Coroutine) -> Generator:
    while True:
        item = yield
        print(label, item)
        target.send(item)

注意:正如文档中也提到的那样,如果要使用更精确的语法来注释生成器类型,则可以使用以下语法:

Generator[YieldType, SendType, ReturnType]

了解更多:https://docs.python.org/3/library/typing.html#typing.Generator