Python 3.4 中的“异步 for”
Posted
技术标签:
【中文标题】Python 3.4 中的“异步 for”【英文标题】:`async for` in Python 3.4 【发布时间】:2016-03-24 03:40:50 【问题描述】:有没有办法在 Python 3.4 代码中转换 Python 3.5 async for
语句?
PEP 0492 说async for
async for TARGET in ITER:
BLOCK
else:
BLOCK2
等价于
iter = (ITER)
iter = type(iter).__aiter__(iter)
running = True
while running:
try:
TARGET = await type(iter).__anext__(iter)
except StopAsyncIteration:
running = False
else:
BLOCK
else:
BLOCK2
但__aiter__
在 Python 3.4 中不存在
【问题讨论】:
如果你有一个工作的 Python 3.5 代码然后查看.__aiter__()
和 .__anext__()
方法的来源(不同的 ITER 可能不同)。
@OldBunny2800 我相信你正在寻找这个***.com/questions/30191556/…
【参考方案1】:
不,没有,async/await
(__aiter__
等)是在 python 3.5 中引入的。在 py3.4 上最接近的是asyncio.gather(如果您可以一次/并行运行所有任务并等待它们全部完成)或将结果推送到asyncio.Queue(这是顺序的,就像async for
) .编辑:如问题中所述,请参阅async for
替代方案的最后一个示例。
这是 asyncio.gather 的示例 ala python 文档:
import asyncio
@asyncio.coroutine
def task(id):
print("task: ".format(id))
yield from asyncio.sleep(random.uniform(1, 3))
return id
tasks = [
task("A"),
task("B"),
task("C")
]
loop = asyncio.get_event_loop()
results = loop.run_until_complete(asyncio.gather(*tasks))
loop.close()
print(results)
输出:
task: B
task: A
task: C
['A', 'B', 'C']
这是 asyncio.Queue 的一个:
import asyncio
@asyncio.coroutine
def produce(queue, n):
for x in range(n):
print('producing /'.format(x, n))
# todo: do something more useful than sleeping :)
yield from asyncio.sleep(random.random())
yield from queue.put(str(x))
@asyncio.coroutine
def consume(queue):
while True:
item = yield from queue.get()
print('consuming ...'.format(item))
# todo: do something more useful than sleeping :)
yield from asyncio.sleep(random.random())
queue.task_done()
@asyncio.coroutine
def run(n):
queue = asyncio.Queue()
# schedule the consumer
consumer = asyncio.ensure_future(consume(queue))
# run the producer and wait for completion
yield from produce(queue, n)
# wait until the consumer has processed all items
yield from queue.join()
# the consumer is still awaiting for an item, cancel it
consumer.cancel()
loop = asyncio.get_event_loop()
loop.run_until_complete(run(10))
loop.close()
编辑:async for
问题中描述的替代方案:
import asyncio
import random
class StopAsyncIteration(Exception):
""""""
class MyCounter:
def __init__(self, count):
self.count = count
def __aiter__(self):
return self
@asyncio.coroutine
def __anext__(self):
if not self.count:
raise StopAsyncIteration
return (yield from self.do_something())
@asyncio.coroutine
def do_something(self):
yield from asyncio.sleep(random.uniform(0, 1))
self.count -= 1
return self.count
@asyncio.coroutine
def getNumbers():
i = MyCounter(10).__aiter__()
while True:
try:
row = yield from i.__anext__()
except StopAsyncIteration:
break
else:
print(row)
loop = asyncio.get_event_loop()
loop.run_until_complete(getNumbers())
loop.close()
请注意,这可以通过删除 __aiter__
和 __anext__
并在 do_something
方法本身内引发停止异常或在完成时返回标记结果来简化(通常是无效值,例如:None
、@ 987654336@、-1
等)
【讨论】:
如果异步函数返回一个可迭代对象,您能否将其分配给变量并使用标准 for-in 循环对其进行迭代? 是的,如果你有类似result = await somecoro()
和somecoro
的东西返回一个可迭代对象(即:list、tuple、dict、set 等),那么当然,你可以稍后对其进行迭代。这里的问题是关于迭代异步迭代器,例如,一个异步迭代器发出一堆 HTTP 请求,并在一个可用时立即产生每个请求的内容,而不必等待所有请求完成。
我为asyncio.gather
和asyncio.Queue
添加了一些示例。当然,如果您使用的是 py3.5,那么异步迭代器会比队列更好(如更简单/更具可读性),至少在我能想到的大多数情况下。
@OldBunny2800 我想到了另一种方法,并作为示例添加。它基本上是 OP 问题中显示的等效代码。
恕我直言,第三个更简单明了,非常好。反正我对__aiter__
的作用还不是很了解。在所有示例中,我发现它总是返回self
。何时将 __aiter__
设置为不同的对象?以上是关于Python 3.4 中的“异步 for”的主要内容,如果未能解决你的问题,请参考以下文章