2015-12-17 16 views
16

C'è un modo per trasformare un'istruzione Python 3.5 async for in un codice Python 3.4?`async for` in Python 3.4

PEP 0492 dice che async for

async for TARGET in ITER: 
    BLOCK 
else: 
    BLOCK2 

è equivalente a

iter = (ITER) 
iter = await type(iter).__aiter__(iter) 
running = True 
while running: 
    try: 
     TARGET = await type(iter).__anext__(iter) 
    except StopAsyncIteration: 
     running = False 
    else: 
     BLOCK 
else: 
    BLOCK2 

ma __aiter__ non esiste in Python 3.4

+2

Se si dispone di un codice Python 3.5 funzionante, controllare il codice sorgente dei metodi '.__ aiter __()' e '.__ anext __()' (potrebbe essere diverso per ITER diversi). – jfs

+0

@ OldBunny2800 Credo che stiate cercando questo https://stackoverflow.com/questions/30191556/coroutine-in-python- tra-3-4-and-3-5-how-can-i-keep-backwords- compatibilità –

risposta

7

No, non c'è, async/await (__aiter__, ecc pure) è stato introdotto in Python 3.5. Su py3.4 la cosa più vicina è asyncio.gather (se è possibile eseguire tutte le attività contemporaneamente/in parallelo e attendere fino a quando non sono tutte terminate) o spingere i risultati in un asyncio.Queue (che è sequenziale, proprio come async for). Modifica: vedi l'ultimo esempio di un'alternativa async for, come descritto nella domanda.

Ecco un esempio ala documentazione Python per 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) 

uscita:

task: B 
task: A 
task: C 
['A', 'B', 'C'] 

Ecco uno per 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 come descritto nella domanda:

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() 

Si noti che questo può essere semplificata eliminando sia __aiter__ e __anext__ e sollevando un'eccezione di arresto all'interno del metodo do_something stessa o restituire un risultato sentinella quando fatto (di solito un valore non valido come: None, "", -1, ecc)

+0

Se una funzione asincrona restituisce un iterabile, sarebbe possibile assegnarlo a una variabile e utilizzare un ciclo for-in standard per iterare su di esso? – OldBunny2800

+0

Sì, se avete qualcosa come 'result = wait somecoro()' e 'somecoro' restituisce un iterable (cioè: list, tuple, dict, set, ecc), quindi sicuro, è possibile iterarlo in seguito. La domanda qui era di iterare su un iteratore asincrono, uno che per esempio fa un sacco di richieste HTTP e produce il contenuto di ciascuna di esse non appena ne è disponibile una, invece di dover aspettare che tutte le completino. – nitely

+1

Ho aggiunto alcuni esempi per 'asyncio.gather' e' asyncio.Queue'. Naturalmente, se si è su py3.5 un iteratore asincrono sarebbe meglio (come in più semplice/più leggibile) di una coda, almeno nella maggior parte delle situazioni a cui riesco a pensare. – nitely