web-dev-qa-db-ja.com

asyncioのループで待機しない方法は?

以下は、asyncioとaiohttpを使用して複数のWebサイトからホームページをダウンロードするおもちゃの例です。

import asyncio
import aiohttp

sites = [
    "http://google.com",
    "http://reddit.com",
    "http://wikipedia.com",
    "http://afpy.org",
    "http://httpbin.org",
    "http://stackoverflow.com",
    "http://reddit.com"
]


async def main(sites):
    for site in sites:
        download(site)


async def download(site):
    response = await client.get(site)
    content = await response.read()
    print(site, len(content))


loop = asyncio.get_event_loop()
client = aiohttp.ClientSession(loop=loop)
content = loop.run_until_complete(main(sites))
client.close()

実行すると、次のようになります。

RuntimeWarning: coroutine 'download' was never awaited

しかし、私はそれを待ちたくありません。

ツイストで私は行うことができます:

for site in sites:
    download(site)

そして、明示的に "yield"しないか、返されたDeferredにコールバックを追加しない場合、ブロックも文句もなしに実行されます。結果にアクセスできませんが、この場合は必要ありません。

JSで私はできる:

site.forEarch(site){
    donwload(site)
}

繰り返しになりますが、ブロックすることはなく、私の側から何かを要求することもありません。

私は方法を見つけました:

async def main(sites):
    await asyncio.wait([download(site) for site in sites])

だが:

  • これを見つけるのは本当に明白ではありません。覚えにくいです。
  • それが何をするのか理解するのは難しいです。 「待機する」は「私はブロックする」と言っているようですが、コルーチンのリスト全体が完了するまでブロックすることを明確に伝えていません。
  • あなたはジェネレータを渡すことができません、それは本当のリストである必要があります、それは私はPythonでは本当に不自然に感じます。
  • 待ち受けが1つしかない場合はどうなりますか?
  • タスクをまったく待ちたくなく、実行するようにスケジュールして、残りのコードを続行したい場合はどうすればよいですか?
  • それはツイストされたJSソリューションよりもはるかに詳細です。

それより良い方法がありますか?

15
e-satis

コルーチンをタスクとしてスケジュールするには、 asyncio.ensure_future を使用します。

for site in sites:
    coro = download(site)
    future = asyncio.ensure_future(coro)

バージョン3.4.4で廃止された関数 asyncio.async を置き換えます。

次に、awaitasyncio.wait または asyncio.gather を使用して、これらの先物を管理できます。

9
Vincent