Как аннотировать декоратор на генераторе, используя подсказки типа? - PullRequest
0 голосов
/ 05 февраля 2019

Я работаю с генераторами как сопрограммы, как описано в превосходном триплете презентаций Дэвида Бизли (на 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, если это имеет значение.)

1 Ответ

0 голосов
/ 05 февраля 2019

Более конкретно, вот несколько вещей, которые вы можете сделать:

  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

...