2016-09-30 8 views
4

私はPythonのコルーチンを(一般的に)理解しようとしています。理論、概念、およびいくつかの例について読んだことがありますが、私はまだ苦労しています。私は非同期モデルを理解しています(ちょっとしたツイストを行いました)が、コルーチンはまだありません。これはどのようにコルーチンですか?

一つtutorialは(私は私の問題を説明するためにいくつかの変更を加えた)コルーチンの例としてこれを与える:

async def download_coroutine(url, number): 
    """ 
    A coroutine to download the specified url 
    """ 
    request = urllib.request.urlopen(url) 
    filename = os.path.basename(url) 
    print("Downloading %s" % url) 

    with open(filename, 'wb') as file_handle: 
     while True: 
      print(number) # prints numbers to view progress 
      chunk = request.read(1024) 
      if not chunk: 
       print("Finished") 
       break 
      file_handle.write(chunk) 
    msg = 'Finished downloading {filename}'.format(filename=filename) 
    return msg 

これは、私ができる発電機のコルーチンの例を見てみると、この

coroutines = [download_coroutine(url, number) for number, url in enumerate(urls)] 
completed, pending = await asyncio.wait(coroutines) 

で実行されますいくつかを参照してくださいyieldステートメント。ここには何もありません。urllibは同期的です、AFAIK。

また、コードは非同期であるため、一連のインターリーブされた数字が表示されることが予想されます。 (1、4、5、1、2、...、 "Finished"、...)。私が見ているのは、Finishedで終わる1つの数字の繰り返しと、もう1つ(3,3,3,3、... "Finished"、1,1,1,1、...、 "Finished"です。 ..)。

この時点で私はチュートリアルが間違っていると言いたいと思います。これはちょうど前に非同期があるので、これはコルーチンです。

+2

あなたは 'async def'を使用しているので、ちょうどコルーチン*です。それは決して他の共同作業に結びついていないので、それほど協力的ではありません。はい、あなたの分析は正しいです。 –

+0

私は最初にそのチュートリアルを書きましたが間違いました。 'aiohttp'を使用するように更新されました –

答えて

12

協力の略共同でコルーチン。他のルーチンへの降伏は、実際にルーチンをコルーチンにします。なぜなら、待機時に降伏するだけで他のコルーチンをインターリーブすることができるからです。新しいasync Python 3.5以降の世界では、通常はawaitによって達成されます。他のコルーチンの結果です。

この定義では、コードはではなく、コルーチンです。 までのPythonは、コルーチンオブジェクトです。これは、async defを使って作成された関数オブジェクトに与えられた型です。

したがって、チュートリアルは、コルーチン関数内で完全に同期した非協調コードを使用していたため、役に立たないです。

urllibの代わりに、非同期HTTPライブラリが必要です。 aiohttpのように:再びセッションを閉じるときだけでなく、より多くのネットワークデータを待っているときに接続が確立されるのを待っている、とするとき

import aiohttp 

async def download_coroutine(url): 
    """ 
    A coroutine to download the specified url 
    """ 
    filename = os.path.basename(url) 
    async with aiohttp.ClientSession() as session: 
     async with session.get(url) as resp: 
      with open(filename, 'wb') as fd: 
       while True: 
        chunk = await resp.content.read(1024) 
        if not chunk: 
         break 
        fd.write(chunk) 
    msg = 'Finished downloading {filename}'.format(filename=filename) 
    return msg 

このコルーチンは、他のルーチンに得ることができます。

さらに、ファイルを非同期に書き込むこともできますが、それはhas portability issuesです。 aiofiles projectライブラリはスレッドを使用してブロック呼び出しをオフロードします。

import aiofiles 

async with aiofiles.open(filename, 'wb') as fd: 
    while True: 
     chunk = await resp.content.read(1024) 
     if not chunk: 
      break 
     await fd.write(chunk) 

:そのライブラリを使用して、コードがに更新する必要がありますブログの記事は、以来、これらの問題を修正するために更新されました。

+0

私は長い時間python2.7プログラマーです。何らかの理由で、新しいpythonを避けています。私は元のチュートリアルとこれらのSOのエントリが非常に役立つことがわかりました。私はこの質問/答えはより多くのupvotesを持っていないことに困惑しています。対象エリアは特化していますか?よくやった! – Greg

+0

@Greg:コルーチンはPythonに比較的新しく追加されたものです。 –

関連する問題