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

Python 如何使用类型暗示在生成器上注释装饰器?,python,python-3.x,generator,type-hinting,Python,Python 3.x,Generator,Type Hinting,正如David Beazley(at)在优秀的三连载演示文稿中所描述的那样,我将生成器作为协同程序来使用,我不知道如何键入装饰者消费者。以下是我目前掌握的情况: from typing import Any, Callable, Generator, Iterable ArbGenerator = Generator[Any, Any, Any] def consumer(fn: ❓) -> ❓: @wraps(fn) def start(*args: Any) -&g

正如David Beazley(at)在优秀的三连载演示文稿中所描述的那样,我将生成器作为协同程序来使用,我不知道如何键入装饰者
消费者
。以下是我目前掌握的情况:

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
。(问题是,如果没有键入(decorator)函数
consumer
本身,我不确定
mypy
是否正在使用该decorator分析任何生成器函数,因此我不确定
ArbGenerator

我对最紧的类型感兴趣,它比
任何
都好,因此当我构建这些协同程序的链时
mypy
会在链设置不正确时给我很好的警告


(Python 3.5,如果有必要的话。)

作为一种更具体的方式,您可以做以下几件事:

  • 使用
    Callable
    类型而不是问号

  • 目标使用
    键入.Coroutine
    ,然后放下
    生成器

  • 协同程序返回一个生成器,返回类型可以是
    生成器
    或其超类型之一

  • 您应该使用callable而不是问号的原因是
    fn
    一开始应该是一个可调用的对象,这就是为什么要用decorator来包装它。调用对象后将创建
    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]
    

    阅读更多信息:

    我倾向于将显式类型视为可选类型,这似乎是Python社区中最慷慨的类型应用。在这种情况下,对于(未来的)程序员来说,任何类型的输入都可能比单独的duck类型更痛苦,所以我会省略它。@AdamSmith-TBH我认为(省略显式类型)是Python的哲学-所以当我在新的工作(我对Python比较陌生)时,我有点惊讶这种类型的提示甚至存在,而且我的同事正在大量使用它。这对你来说是个好消息!我很想在这里看到一个好的解决方案,因为强显式类型和协同程序对我来说都是弱点,我屏住呼吸看着这个问题:)@davidbak为什么不使用
    typing.Coroutine
    ?@Kasr-mvd-谢谢。出于某种原因,我认为
    Coroutine
    特定于异步。出于某种原因,我认为
    Coroutine
    特定于异步。现在,我一直在运行
    mypy--strict
    ,但是这个解决方案几乎可以与
    mypy--strict一起工作--允许任何泛型
    具有非特定的
    可调用
    corroutine
    生成器
    :我将其编辑为在
    消费者内部包装的
    start
    中提示
    c
    ,需要避免在
    return c`中出现错误,
    Any
    不是一个
    Coroutine
    。我想我想知道为什么来自Coroutine的返回类型不是
    Coroutine
    ,因为它们显然是(
    yield
    在分配的RHS上)?@davidbak,因为它们不返回
    Coroutine
    。事实上,它们返回
    None
    (Python抽象表示没有任何对象)。但是,如果要使生成器的键入更精确,可以使用
    generator[YieldType,SendType,ReturnType]
    语法。在这里阅读更多