web-dev-qa-db-ja.com

非同期ジェネレーターはイテレーターではありませんか?

Pythonでは、次のように反復可能なジェネレータを書くことができます:

def generate(count):
    for x in range(count):
        yield x

# as an iterator you can apply the function next() to get the values.
it = generate(10)
r0 = next(it)
r1 = next(it) ...

非同期イテレータを使用しようとすると、「非同期内部での利回り」エラーが発生します。推奨される解決策は、独自のジェネレータを実装することです。

class async_generator:
    def __aiter__(self):
        return self
    async def __anext__(self):
        await asyncio.sleep()
        return random.randint(0, 10)

# But when you try to get the next element
it = async_generator(10)
r0 = next(it)

「 'async_generator'オブジェクトはイテレータではありません」というエラーが表示される

Iteratorを呼び出す場合、インターフェイスはまったく同じであるため、非同期イテレータを記述して、next()呼び出しに大きく依存するフレームワークで使用できます。非同期を使用できるようにコード全体を書き直す必要がある場合、新しいPython機能は無意味です。

何か不足していますか?

ありがとう!

19
user1275011

したがって、@ bosnjakが言ったように、asyncを次の目的で使用できます。

async for ITEM in A_ITER:
    BLOCK1
else: # optional
    BLOCK2

しかし、手動で反復したい場合は、次のように書くだけです。

it = async_iterator()
await it.__anext__()

しかし、私はそうすることをお勧めしません。

何か同じイテレータを持っているので、イテレータを呼び出す場合、非同期イテレータを記述して、next()呼び出しに大きく依存するフレームワークで使用できると思います

いいえ、実際には同じではありません。通常の同期イテレータと非同期イテレータには違いがあります。そしてそれにはいくつかの理由があります:

  1. Pythonコルーチンは内部的にジェネレーターの上に構築されています
  2. PythonのZenによると、明示的は暗黙的よりも優れています。実際に表示されるように、コードを一時停止できます。

そのため、非同期イテレータでiterおよびnextを使用することは不可能です。また、同期イテレータを想定しているフレームワークでは使用できません。したがって、コードを非同期にする場合は、非同期フレームワークも使用する必要があります。 ここ はその一部です。

また、イテレータとジェネレータについて簡単に説明します。イテレータは__iter__および__next__メソッドを持つ特別なオブジェクトです。一方、ジェネレータはyield式を含む特別な関数です。 すべてのジェネレータはイテレータですが、その逆はありません。非同期のイテレータとジェネレータでも同じことが受け入れられます。はい、python 3.6なので、非同期ジェネレータを作成できます!

async def ticker(delay, to):
    for i in range(to):
        yield i
        await asyncio.sleep(delay)

詳細は PEP 525 を参照してください。

10

新しいステートメントが非同期ジェネレーターに導入されたと思います:

async for TARGET in ITER:
    BLOCK
else:
    BLOCK2

PEP 492 によると。

基本的に、これはあなたがすべきことを意味します:

async for number in generate(10):
        print(number)

また、 ジェネレーターとの違い も確認してください。

ネイティブコルーチンオブジェクトは、iterおよびnextメソッドを実装していません。したがって、それらを反復したり、iter()、list()、Tuple()やその他の組み込み関数に渡すことはできません。また、for..inループでも使用できません。 iterまたはnextをネイティブコルーチンオブジェクトで使用しようとすると、 TypeErrorで。

5
bosnjak

これを使用してリストをループして非同期

class AsyncRange(object):
    def __init__(self, length):
        self.length = length
        self.i = 0

    async def __aiter__(self):
        return self

    async def __anext__(self):
        index = self.i
        self.i += 1
        if self.i <= self.length:
            return index
        else:
            raise StopAsyncIteration

次に単に:

async for i in AsyncRange(my_list):
    # your code
1
Juggernaut