python:遍历列表或异步生成器

python: iterate over either a list or an async generator

自从迭代器在 python 中被引入以来,总是可以不关心你是在处理迭代器还是列表:

from random import random

def gen_list():
    print('gen')
    for i in range(10):
        yield i

def return_list():
    print('return')
    return [i for i in range(10)]


if random() > 0.5:
    x = gen_list()
else:
    x = return_list()

for i in x:
    pass

PEP 492 引入了 asynchronous iteratorsasync for 语法。我看不到为异步迭代器的使用者添加语法的新负担的任何理由。

在我的代码中,我有时会处理一个列表(来自缓存),有时会处理一个异步生成器:

import asyncio
from random import random

def is_small_and_in_cache():
    if random() > 0.5:
        print('in fake cache')
        return [i for i in range(10)]

async def get_progressively():
    print('gen')
    for i in range(10):
        # e.g. an await here
        await asyncio.sleep(0.1)
        yield i

async def main():
    x = is_small_and_in_cache()
    if x is None:
        x = get_progressively()

    async for i in x:
        pass

asyncio.run(main())

但是上面的失败(一半的时间)是 TypeError: 'async for' requires an object with __aiter__ method, got list

主要问题:这个要怎么写才能处理呢?我应该尝试将列表转换为虚拟异步生成器,还是包装异步生成器以生成列表?

支线任务:是否有任何建议可以摆脱(对我来说显然不pythonic)async for结构,即为什么可以常规 for 循环不处理异步生成器吗? Python3x 是否在可用性方面失去了它的方式?

语法的存在是为了警告你你的“循环”实际上可能包括暂停你的整个调用,允许其他代码运行,所以你知道有在每次迭代的顶部处于一致状态的适当数据。它哪儿也去不了。

当然,协同程序 没有 暂停,您可以使用它来使 wrapping 任何可迭代的琐碎:

async def desync(it):
  for x in it: yield x

这比相反的数字更有用,相反的数字仍然是异步的,因为它必须收集到一个列表中:

async def gather(ai):
  ret=[]
  async for x in ai: ret.append(x)
  return ret

因为它允许在完全异步的情况下进行适当的交错。