ジェネレーターとイテレーター、および__next__()
の役割について読んでいました。
'__next__' in dir(mygen)
。本当です
'__next__' in dir(mylist)
、false
深く調べてみると、
'__next__' in dir (mylist.__iter__())
はtrueです
なぜis ___next__
_はリストでのみ使用でき、__iter__()
とmygen
でのみ使用でき、mylist
では使用できません。リスト内包表記を使用してリストをステップスルーするときに、__iter__()
はどのように___next__
_を呼び出しますか
ジェネレーターを手動でステップアップ(+1)しようとして、mygen.__next__()
を呼び出しました。存在しません。メソッドラッパーと呼ばれる_mygen.__next__
_としてのみ存在します。
メソッドラッパーとは何ですか?それは何をしますか?ここではどのように適用されますか:in mygen() and __iter__() ?
___next__
_がジェネレーターとイテレーターの両方が提供するもの(およびそれらの唯一のプロパティ)である場合、ジェネレーターとイテレーターの違いは何ですか?*
3への回答:mod/editorが指摘したように、解決しました:
更新:ジェネレーターとイテレーターの両方に__next__()
があります。私の間違い。ログを見ると、どういうわけかmygen.__next__()
テストで停止例外エラーが発生していました。しかし、私はそのエラーを再び再現することができませんでした。
答えてくれてありがとう!
特別なメソッド___iter__
_および___next__
_は、作成するイテレータープロトコルの一部です イテレータータイプ 。この目的のために、2つの別々のものを区別する必要があります:Iterablesとiterators。
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
_
_
__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__
(list
はC
に実装されたオブジェクトです)と_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__
_メソッド記述子があります。リスト自体はジェネレーターではないため、このメソッドはありません。
ジェネレーターはイテレーターです。チェックアウト ジェネレーターとイテレーターの違い