8

私は最近 asyncio をいじっていますが、それがどのように機能するかについて直感をつかみ始めていますが、できていないことがあります。構造が間違っているからなのか、やろうとしていることが意味をなさない理由があるのか​​ はわかりません。

要するに、私は降伏する asyncio.coroutine を反復処理できるようにしたいと考えています。たとえば、次のようなことができるようになりたいです。

@asyncio.coroutine
def countdown(n):
    while n > 0:
        yield from asyncio.sleep(1)
        n = n - 1
        yield n

@asyncio.coroutine
def do_work():
    for n in countdown(5):
        print(n)

loop.run_until_complete(do_work())

ただし、これは asyncio の腸から例外をスローします。私は他のことを試しましfor n in (yield from countdown(5)): ...たが、それも同様に不透明なランタイム例外を与えます。

なぜあなたがこのようなことをしてはいけないのかすぐにはわかりませんが、何が起こっているのかを理解する能力の限界に達しています。

そう:

  • これを行うことが可能である場合、どうすればそれを行うことができますか?
  • それが不可能なら、なぜですか?

この質問が明確でない場合はお知らせください。

4

3 に答える 3

5

asyncio コルーチンでは、yield fromand neverを使用する必要がありyieldます。それは設計によるものです。の引数yield fromは、別のコルーチンまたはasyncio.Futureインスタンスのみにする必要があります。

コルーチン自体の呼び出しは、yield fromlike で再度使用する必要がありますyield from countdown(5)

あなたの場合、キューを使用することをお勧めします。

import asyncio

@asyncio.coroutine
def countdown(n, queue):
    while n > 0:
        yield from asyncio.sleep(1)
        n = n - 1
        yield from queue.put(n)
    yield from queue.put(None)

@asyncio.coroutine
def do_work():
    queue = asyncio.Queue()
    asyncio.async(countdown(5, queue))
    while True:
        v = yield from queue.get()
        if v:
            print(v)
        else:
            break

asyncio.get_event_loop().run_until_complete(do_work())

さて、によって生成された値のチェックを使用できますcountdown。次の例は機能します。しかし、それはアンチパターンだと思います:

  1. めちゃくちゃ簡単すぎる

  2. とにかくcountdown、関数などで呼び出しを作成することはできませんitertoolssum(countdown(5))またはのようなものを意味しますitertools.accumulate(countdown(5))

とにかく、ミキシングyieldyield fromコルーチンの例:

import asyncio

@asyncio.coroutine
def countdown(n):
    while n > 0:
        yield from asyncio.sleep(1)
        n = n - 1
        yield n

@asyncio.coroutine
def do_work():
    for n in countdown(5):
        if isinstance(n, asyncio.Future):
            yield from n
        else:
            print(n)

asyncio.get_event_loop().run_until_complete(do_work())
于 2014-05-17T12:47:53.833 に答える
4

Python 3.5 では、async for構文が導入されました。ただし、非同期イテレータ関数の構文はまだありません (つまり、関数yieldでは禁止されていasyncます)。回避策は次のとおりです。

import asyncio
import inspect

class escape(object):
    def __init__(self, value):
        self.value = value

class _asynciter(object):
    def __init__(self, iterator):
        self.itr = iterator
    async def __aiter__(self):
        return self
    async def __anext__(self):
        try:
            yielded = next(self.itr)
            while inspect.isawaitable(yielded):
                try:
                    result = await yielded
                except Exception as e:
                    yielded = self.itr.throw(e)
                else:
                    yielded = self.itr.send(result)
            else:
                if isinstance(yielded, escape):
                    return yielded.value
                else:
                    return yielded
        except StopIteration:
            raise StopAsyncIteration

def asynciter(f):
    return lambda *arg, **kwarg: _asynciter(f(*arg, **kwarg))

次に、コードは次のように記述できます。

@asynciter
def countdown(n):
    while n > 0:
        yield from asyncio.sleep(1)
        #or:
        #yield asyncio.sleep(1)
        n = n - 1
        yield n

async def do_work():
    async for n in countdown(5):
        print(n)

asyncio.get_event_loop().run_until_complete(do_work())

新しい構文とこのコードがどのように機能するかについて学ぶには、PEP 492を参照してください。

于 2015-09-09T13:14:46.010 に答える
1

更新: python 3.5はこれをよりネイティブにサポートしているようです:

同じ問題で立ち往生している (そしてaio-s3 のコードに触発されている) ため、より洗練された解決策があるはずだと感じました。

import asyncio

def countdown(number):
    @asyncio.coroutine
    def sleep(returnvalue):
        yield from asyncio.sleep(1)
        return returnvalue
    for n in range(number, 0, -1):
        yield sleep(n)

@asyncio.coroutine
def print_countdown():
    for future in countdown(5):
        n = yield from future
        print ("Counting down: %d" % n)

asyncio.get_event_loop().run_until_complete(print_countdown())

理論的根拠:countdownメソッドは先物を生成します。それぞれは、提供された数に 1 秒間スリープした後に解決されます。

print_countdown関数は最初の未来を取り、それyield fromを -ing (解決されるまで一時停止します) し、意図した結果を取得します: n.

于 2015-09-02T14:28:53.727 に答える