web-dev-qa-db-ja.com

ジェネレーターとイテレーターの__next__とメソッドラッパーとは何ですか?

ジェネレーターとイテレーター、および__next__()の役割について読んでいました。

'__next__' in dir(mygen)。本当です

'__next__' in dir(mylist)、false

深く調べてみると、

'__next__' in dir (mylist.__iter__())はtrueです

  1. なぜis ___next___はリストでのみ使用でき、__iter__()mygenでのみ使用でき、mylistでは使用できません。リスト内包表記を使用してリストをステップスルーするときに、__iter__()はどのように___next___を呼び出しますか

    ジェネレーターを手動でステップアップ(+1)しようとして、mygen.__next__()を呼び出しました。存在しません。メソッドラッパーと呼ばれる_mygen.__next___としてのみ存在します。

  2. メソッドラッパーとは何ですか?それは何をしますか?ここではどのように適用されますか:in mygen() and __iter__() ?

  3. ___next___がジェネレーターとイテレーターの両方が提供するもの(およびそれらの唯一のプロパティ)である場合、ジェネレーターとイテレーターの違いは何ですか?*

    3への回答:mod/editorが指摘したように、解決しました:

    Pythonのジェネレーターとイテレーターの違い

更新:ジェネレーターとイテレーターの両方に__next__()があります。私の間違い。ログを見ると、どういうわけかmygen.__next__()テストで停止例外エラーが発生していました。しかし、私はそのエラーを再び再現することができませんでした。

答えてくれてありがとう!

6
theMobDog

特別なメソッド___iter___および___next___は、作成するイテレータープロトコルの一部です イテレータータイプ 。この目的のために、2つの別々のものを区別する必要があります:Iterablesiterators

Iterablesは反復可能なものであり、通常、これらはアイテムを含むある種のコンテナ要素です。一般的な例は、リスト、タプル、または辞書です。

イテレータを反復処理するには、iteratorを使用します。イテレータは、コンテナを反復処理するのに役立つオブジェクトです。たとえば、リストを反復する場合、イテレータは基本的に、現在どのインデックスにいるかを追跡します。

イテレータを取得するには、イテレータで___iter___メソッドを呼び出します。これは、この特定のイテレータの新しいイテレータを返すファクトリメソッドのようなものです。 ___iter___メソッドが定義されている型は、それを反復可能に変換します。

イテレーターには通常、単一のメソッド___next___が必要です。このメソッドは、反復のnextアイテムを返します。さらに、プロトコルを使いやすくするために、すべてのイテレータもイテレータであり、___iter___メソッドでそれ自体を返す必要があります。

簡単な例として、これはリストの可能なイテレータ実装になります。

_class ListIterator:
    def __init__ (self, lst):
        self.lst = lst
        self.idx = 0

    def __iter__ (self):
        return self

    def __next__ (self):
        try:
            item = self.lst[self.idx]
        except IndexError:
            raise StopIteration()
        self.idx += 1
        return item
_

リストの実装では、___iter___メソッドからListIterator(self)を返すだけで済みます。もちろん、リストの実際の実装はCで行われるため、これは少し異なって見えます。しかし、考え方は同じです。

イテレータは、Pythonのさまざまな場所で目に見えない形で使用されます。たとえば、forループ:

_for item in lst:
    print(item)
_

これは、次のようなものです。

_lst_iterator = iter(lst) # this just calls `lst.__iter__()`
while True:
    try:
        item = next(lst_iterator) # lst_iterator.__next__()
    except StopIteration:
        break
    else:
        print(item)
_

したがって、forループは、イテレータオブジェクトからイテレータを要求し、StopIteration例外が発生するまで、そのイテレータで___next___を呼び出します。これが表面下で発生することも、イテレータに___iter___を実装させたい理由です。そうしないと、イテレータをループすることはできません。


ジェネレーターに関しては、人々が通常参照するのは、実際にはジェネレーターfunction、つまりyieldステートメントを持つ関数定義です。そのジェネレーター関数を呼び出すと、generatorが返されます。ジェネレーターは、基本的には単なるイテレーターですが、派手なものです(コンテナー内を移動する以上のことを行うため)。イテレータとして、次の要素を「生成」するための___next___メソッドと、それ自体を返すための___iter___メソッドがあります。


ジェネレーター関数の例は次のとおりです。

_def exampleGenerator():
    yield 1
    print('After 1')
    yield 2
    print('After 2')
_

yieldステートメントを含む関数本体は、これをジェネレーター関数に変換します。つまり、exampleGenerator()を呼び出すと、generatorオブジェクトが返されます。ジェネレータオブジェクトはイテレータプロトコルを実装しているので、その上で___next___を呼び出すことができます(または上記のようにnext()関数を使用します)。

_>>> x = exampleGenerator()
>>> next(x)
1
>>> next(x)
After 1
2
>>> next(x)
After 2
Traceback (most recent call last):
  File "<pyshell#10>", line 1, in <module>
    next(x)
StopIteration
_

最初のnext()呼び出しはまだ何も出力しなかったことに注意してください。これはジェネレーターの特別な点です。ジェネレーターは怠惰であり、反復可能オブジェクトから次のアイテムを取得するために必要なだけ評価します。 2番目のnext()呼び出しでのみ、関数本体から最初の出力行を取得します。また、反復可能オブジェクトを使い果たすには、別のnext()呼び出しが必要です(別の値が生成されないため)。

しかし、その怠惰は別として、ジェネレーターは反復可能なもののように機能します。最後にStopIteration例外が発生することもあります。これにより、ジェネレーター(およびジェネレーター関数)をforループソースとして使用でき、「通常の」反復可能オブジェクトを使用できます。

ジェネレーターとその怠惰の大きな利点は、ものを生成できることですオンデマンド。これの良い例えは、ウェブサイトでの無限のスクロールです。後でアイテムを下にスクロールでき(ジェネレーターでnext()を呼び出す)、ウェブサイトはバックエンドにクエリを実行してさらに取得する必要があります。スクロールするアイテム。理想的には、これはあなたが気付かないうちに起こります。そして、それはまさにジェネレーターが行うことです。それはこのようなことさえ可能にします:

_def counter():
    x = 0
    while True:
        x += 1
        yield x
_

怠惰ではない、これは無限ループであるため、これを計算することは不可能です。しかし、怠惰なことに、ジェネレーターとして、アイテムの後にこの反復的な1つのアイテムを消費することが可能です。私は当初、このジェネレーターを完全にカスタムのイテレーター型として実装することを避けたかったのですが、この場合、これは実際にはそれほど難しくないので、ここで説明します。

_class CounterGenerator:
    def __init__ (self):
        self.x = 0

    def __iter__ (self):
        return self

    def __next__ (self):
        self.x += 1
        return self.x
_
27
poke

___next___はリストでのみ使用でき、__iter__()mygenでのみ使用でき、mylistでは使用できないのはなぜですか。 list-comprehensionを使用してリストをステップ実行する場合、__iter__()はどのように___next___を呼び出しますか。

リストには、反復を処理するためにiterから返される個別のオブジェクトがあるため、このオブジェクト___iter___は連続して呼び出されます。

したがって、リストの場合:

_iter(l) is l # False, returns <list-iterator object at..>
_

一方、ジェネレーターの場合:

_iter(g) is g # True, its the same object
_

ループ構造では、最初にiterが、ループされるターゲットオブジェクトで呼び出されます。 iterは___iter___を呼び出し、イテレータが返されることが期待されます。その___next___は、使用可能な要素がなくなるまで呼び出されます。

メソッドラッパーとは何ですか?それは何をしますか?ここではどのように適用されますか:mygen()および__iter__()で?

メソッドラッパーは、私が間違っていない限り、Cに実装されているメソッドです。これらのiter(list).__iter__listCに実装されたオブジェクトです)と_gen.__iter___(ここではわかりませんが、ジェネレーターもおそらくそうです)はどちらですか。

___next___がジェネレーターとイテレーターの両方が提供するもの(およびそれらの唯一のプロパティ)である場合、ジェネレーターとイテレーターの違いは何ですか?

ジェネレーターは、iter(l)から提供されるイテレーターと同様に、イテレーターです。 ___next___メソッドを提供するためイテレータです(通常、forループで使用すると、使い果たされるまで値を提供できます)。

___next___および___iter___は、next(some_gen)またはiter(some_sequence)を実行するときのメソッドラッパーです。 next(some_gen)some_gen.__next__()と同じです

したがって、mygen = iter(mylist)を実行すると、mygenはジェネレータオブジェクトとしてmylist実装され、___next___メソッド記述子があります。リスト自体はジェネレーターではないため、このメソッドはありません。

ジェネレーターはイテレーターです。チェックアウト ジェネレーターとイテレーターの違い

1
sytech