Смешанное понимание (asyn c + syn c) не работает должным образом в Python - PullRequest
2 голосов
/ 05 августа 2020

Я пытаюсь улучшить свой код, опуская изменяемый список и заменяя понимание. Существует вложенный итератор for, внутренний - asyn c. Вот рабочий код:

async def get_accumulator_providers(order: OrderDTO) -> Tuple[str, ...]:
    bucket_mapping = await read_config(order)
    result = []
    for bucket in bucket_mapping:
        async for item in dispatch_bucket(order, bucket, bucket_mapping[bucket], frozenset(accumulator_filters)):
            result.append(item)
    return tuple(result)

Но когда я использую этот код вместо:

async def get_accumulator_providers(order: OrderDTO) -> Tuple[str, ...]:
    bucket_mapping = await read_config(order)

    return tuple(item for bucket in bucket_mapping async for item in
             dispatch_bucket(order, bucket, bucket_mapping[bucket], frozenset(accumulator_filters)))

, я получаю эту ошибку:

 'async_generator' object is not iterable

В чем проблема с моим вложенным пониманием?

Ответы [ 2 ]

1 голос
/ 05 августа 2020

Проблема здесь в том, что tuple использует протокол синхронного итератора.

Юру Селиванов ответил здесь :

... result = list (await g (i) for i in range (3))

Это эквивалентно этому коду:

 async def ait():
     for i in range(3):
         v = await g(i)
         yield v

 result = list(ait())

Где ait - асинхронный c функция генератора. Вы не можете повторить его с помощью обычного синтаксиса for x in ... и не можете передать его функциям, которые ожидают синхронного итератора (например, list).

Аналогично, с синхронным кодом:

 a = (i for i in range(3))
 a[0]
 Traceback (most recent call last):
   File "<stdin>", line 1, in <module>
 TypeError: 'generator' object is not subscriptable

, где (for ...) - другой синтаксис для определения синхронного генератора.

... result = [await g(i) for i in range(3)]

Это эквивалентно этот код:

 result = []
 for i in range(3):
     v = await g(i)
     result.append(v)

Я согласен с тем, что PEP 530 немного расплывчато об этом и может быть обновлен. Я займусь этим.

Возможно, мы сможем сделать сообщение об ошибке «TypeError: 'async_generator' не повторяется» немного яснее. Приветствуются любые идеи по его улучшению.

Я бы сказал, что первый случай должен либо вести себя как второй, либо вызывать синтаксическую ошибку.

Нет, но мы можем улучшить сообщения об ошибках.

0 голосов
/ 05 августа 2020

Как упоминалось в @VisioN, и я обнаружил здесь , проблема не в смешивании asyn c и syn c для циклов. Проблема заключается в использовании tuple () для объединения итераций. Использование [] в качестве понимания списка устранило проблему. На самом деле tuple () не является пониманием списка, а просто получает генератор и вызывает iter для создания кортежа. Следовательно, работает следующий код:

async def get_accumulator_providers(order: OrderDTO) -> Tuple[str, ...]:
bucket_mapping = await read_config(order)

return tuple([item for bucket in bucket_mapping async for item in
         dispatch_bucket(order, bucket, bucket_mapping[bucket], frozenset(accumulator_filters))])
...