【问题标题】:How to annotate a decorator on a generator using type hinting?如何使用类型提示在生成器上注释装饰器?
【发布时间】:2019-02-05 06:10:53
【问题描述】:

我正在使用作为协同程序的生成器,正如 David Beazley(http://www.dabeaz.com/coroutines/)的精彩演讲中所描述的那样,我不知道如何键入装饰器 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。)

【问题讨论】:

  • 我倾向于将显式类型视为可选,这似乎是 Python 社区中最慷慨的类型应用。在这种情况下,对于(未来的)程序员来说,any 类型可能比单独使用鸭子类型更痛苦,所以我会省略它。
  • @AdamSmith - TBH 我认为那(省略显式输入)是 Python 的哲学 - 所以当我在我的新工作(我对 Python 相对较新)时,我有点惊讶,输入提示甚至存在并且我的同事正在大量使用它。
  • 这对你来说是个好消息!我很想在这里看到一个好的解决方案,因为强显式类型和协程对我来说都是弱点,所以我屏住呼吸看着这个问题:)
  • @davidbak 你为什么不用typing.Coroutine
  • @Kasrâmvd - 谢谢。出于某种原因,我认为 Coroutine 特定于异步。

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


【解决方案1】:

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

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

  2. typing.Coroutine 用于targets 并删除ArbGenerator

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

你应该使用 callable 而不是问号的原因是 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

【讨论】:

  • 出于某种原因,我认为Coroutine 是特定于异步的。现在,我一直在运行mypy --strict,但这个解决方案几乎可以与mypy --strict --allow-any-generics 一起使用,以拥有非特定的CallableCoroutineGenerator:我编辑它以在包装的start 中提示c consumer, needed to avoid an error at return c` 的内部是 Any 不是 Coroutine
  • 我想我想知道为什么协程的返回类型不是Coroutine,因为它们显然是(yield on RHS of assignment)?
  • @davidbak 因为他们不返回Coroutine。事实上,它们返回None(没有任何对象的Python抽象)。然而,如果你想让生成器的输入更精确,你可以使用Generator[YieldType, SendType, ReturnType] 语法。在此处阅读更多信息docs.python.org/3/library/typing.html#typing.Generator
猜你喜欢
  • 2022-09-21
  • 2019-11-13
  • 2020-03-04
  • 2020-08-10
  • 2018-04-14
  • 2021-11-27
  • 1970-01-01
  • 2019-07-23
相关资源
最近更新 更多