2015-12-17 41 views
16

Istnieje sposób na przekształcenie instrukcji Python 3.5 async for w kodzie Python 3.4?`async for` w języku Python 3.4

PEP 0492 mówi, że async for

async for TARGET in ITER: 
    BLOCK 
else: 
    BLOCK2 

jest równoważna

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 

ale __aiter__ nie istnieje w Pythonie 3.4

+2

Jeśli masz działający kod Pythona 3.5, spójrz na źródło metod '.__ aiter __()' i '.__ anext __()' (może być inne dla różnych ITER). – jfs

+0

@ OldBunny2800 Uważam, że szukasz tego https://stackoverflow.com/questions/30191556/coroutine-in-python-between-3-4-and-3-5-how-can-i-keep-backwords- zgodność –

Odpowiedz

7

Nie, nie ma, async/await (__aiter__, etc, jak również) został wprowadzony w pythonie 3.5. Na py3.4 najbliższą rzeczą jest asyncio.gather (jeśli możesz uruchomić wszystkie zadania naraz/równolegle i poczekać, aż wszystkie zostaną zakończone) lub przesłać wyniki do asyncio.Queue (która jest sekwencyjna, tak jak async for). Edycja: zobacz ostatni przykład alternatywnej alternatywy, jak opisano w pytaniu.

Oto przykład ala docs Pythona do 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) 

wyjściowa:

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

Oto jeden dla 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 alternatywa opisana w pytaniu:

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

Należy zauważyć, że może być uproszczona przez usunięcie zarówno __aiter__ i __anext__ i podnoszenie wyjątek stop w obrębie samej metody do_something lub zwraca wynik wskaźnikowych po zakończeniu (zwykle nieprawidłowa wartość jak: None, "", -1, etc.)

+0

Jeśli funkcja asynchroniczna zwraca wartość iterowalną, czy byłbyś w stanie przypisać ją do zmiennej i użyć standardowej pętli wejścia do iteracji? – OldBunny2800

+0

Tak, jeśli masz coś takiego jak 'result = czekaj na somecoro()' a 'somecoro' zwraca iterowalne (tj. List, krotka, dict, zestaw, itd.), To na pewno możesz później iterować. Tutaj chodziło o iterację nad asynchronicznym iteratorem, który na przykład tworzy mnóstwo żądań HTTP i dostarcza zawartość każdego z nich, gdy tylko jest dostępny, zamiast czekać na wszystko, aby zakończyć. – nitely

+1

Dodałem kilka przykładów dla 'asyncio.gather' i' asyncio.Queue'. Oczywiście, jeśli korzystasz z py3.5, iterator asynchroniczny będzie lepszy (jak w prostszy/bardziej czytelny) niż kolejka, przynajmniej w większości sytuacji, o której mogę pomyśleć. – nitely