`async for` en Python 3.4
asynchronous async-await (1)
¿Hay una forma de transformar un Python 3.5 async for
declaración en un código Python 3.4?
PEP 0492 dice que async for
async for TARGET in ITER:
BLOCK
else:
BLOCK2
es equivalente a
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
pero __aiter__
no existe en Python 3.4
No, no hay, async/await
__aiter__
( __aiter__
, etc también) se introdujo en Python 3.5. En py3.4, lo más cercano es asyncio.gather (si puede ejecutar todas las tareas a la vez / en paralelo y esperar hasta que finalicen) o asyncio.Queue resultados en un asyncio.Queue (que es secuencial, igual que async for
). Editar: vea el último ejemplo de un async for
alternativa, como se describe en la pregunta.
Aquí hay un ejemplo de ala python docs para asyncio.gather:
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)
Salida:
task: B
task: A
task: C
[''A'', ''B'', ''C'']
Aquí hay uno para 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()
Edit: async for
alternativa como se describe en la pregunta:
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()
Tenga en cuenta que esto se puede simplificar eliminando tanto __aiter__
como __anext__
y elevando una excepción de detención dentro del do_something
método do_something
o devolviendo un resultado de centinela cuando haya terminado (generalmente un valor no válido como: None
, ""
, -1
, etc.)