2015-12-17 19 views
16

Python 3.4のコードでPython 3.5 async for文を変換する方法がありますか?Python 3.4で `async for`を実行する

PEP 0492async for

async for TARGET in ITER: 
    BLOCK 
else: 
    BLOCK2 

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 

と同等であるが、同様いいえ、ありません、async/await__aiter__などのPython 3.4で

+2

作業中のPython 3.5コードをお持ちの場合は、 '.__ aiter __()'と '.__ anext __()'メソッドのソースを見てください(ITERごとに異なる場合があります)。 – jfs

+0

@ OldBunny2800私はあなたがこれを探していると信じていますhttps://stackoverflow.com/questions/30191556/coroutine-in-python-between-3-4-and-3-5-how-can-i-keep-backwords-互換性 –

答えて

7

__aiter__存在しないことを言います)はPython 3.5で導入されました。 py3.4の場合、最も近いのはasyncio.gatherです(すべてのタスクを同時に実行するか、すべて終了するまで待ちます)。または結果をasyncio.Queue(順番に、async for)にプッシュします。編集:質問に記載されているasync for代替の最後の例を参照してください。ここで

はasyncio.gatherのためのpythonのドキュメントALA例です。

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

(のような通常無効値:None""-1など)を行うときは、この__aiter____anext__の両方を除去しdo_something方法自体の中に停止例外を上げることによって簡略化またはセンチネル結果を返すことができることに注意してください

+0

非同期関数が反復可能性を返す場合、それを変数に代入し、標準for-inループを使用して反復処理できますか? – OldBunny2800

+0

はい、 'result = await somecoro()'や 'somecoro'がiterable(リスト、タプル、dict、setなど)を返す場合は、後で繰り返し処理できます。ここでの質問は、非同期イテレータを反復することです。たとえば、一連のHTTPリクエストを作成し、すべてのリクエストが完了するのを待つのではなく、リクエストが利用可能になるとすぐにそれぞれの内容を取得するというものです。 – nitely

+1

'asyncio.gather'と' asyncio.Queue'の例をいくつか追加しました。もちろん、py3.5上にある場合は、非同期イテレータは(少なくともシンプルで/より読みやすいように)キューよりも優れています。少なくともほとんどの状況で考えることができます。 – nitely

関連する問題