イテレータとジェネレータの違いは何ですか?それぞれのケースを使用する場合の例が参考になります。
イテレータとジェネレータの違いは何ですか?それぞれのケースを使用する場合の例が参考になります。
回答:
iterator
はより一般的な概念です。クラスにnext
メソッド(__next__
Python 3の場合)とその__iter__
メソッドが含まれるオブジェクトreturn self
。
すべてのジェネレータはイテレータですが、その逆はありません。ジェネレータは、1つまたは複数のyield
式(yield
Python 2.5以前のステートメント)を含む関数を呼び出すことによって構築され、前の段落の定義に適合するオブジェクトです。iterator
。
やや複雑な状態維持動作を持つクラスが必要な場合、またはnext
(および__iter__
and __init__
)以外の他のメソッドを公開する場合は、ジェネレータではなくカスタムイテレータを使用することをお勧めします。ほとんどの場合、ジェネレーター(時には、十分に単純なニーズのために、ジェネレーター式)で十分です。状態の維持(妥当な制限内)は基本的に、フレームが一時停止および再開されることによって「自動的に行われる」ため、コーディングが簡単です。
たとえば、次のようなジェネレータ。
def squares(start, stop):
for i in range(start, stop):
yield i * i
generator = squares(a, b)
または同等のジェネレータ式(genexp)
generator = (i*i for i in range(a, b))
カスタムイテレータとしてビルドするには、より多くのコードが必要になります。
class Squares(object):
def __init__(self, start, stop):
self.start = start
self.stop = stop
def __iter__(self): return self
def next(self): # __next__ in Python 3
if self.start >= self.stop:
raise StopIteration
current = self.start * self.start
self.start += 1
return current
iterator = Squares(a, b)
ただし、もちろん、クラスSquares
を使用すると、追加のメソッドを簡単に提供できます。
def current(self):
return self.start
アプリケーションでこのような追加機能が実際に必要な場合。
for ... in ...:
関数の一部として渡されるか、関数に渡されるか、呼び出しますiter.next()
for..in
。何かが足りなかったのかもしれませんが、少し前のことですが、解決したかどうかは覚えていません。ありがとうございました!
イテレータとジェネレータの違いは何ですか?それぞれのケースを使用する場合の例が参考になります。
要約すると:イテレータは__iter__
、__next__
(next
Python 2では)メソッドとa を持つオブジェクトです。ジェネレーターは、イテレーターのインスタンスを作成するための簡単な組み込み方法を提供します。
収量が含まれる関数は依然として関数であり、呼び出されるとジェネレーターオブジェクトのインスタンスを返します。
def a_function():
"when called, returns generator object"
yield
ジェネレータ式もジェネレータを返します:
a_generator = (i for i in range(0))
より詳細な説明と例については、読み続けてください。
具体的には、ジェネレーターはイテレーターのサブタイプです。
>>> import collections, types
>>> issubclass(types.GeneratorType, collections.Iterator)
True
ジェネレータはいくつかの方法で作成できます。そのための非常に一般的で簡単な方法は、関数を使用することです。
具体的には、yieldを含む関数は関数であり、呼び出されるとジェネレーターを返します。
>>> def a_function():
"just a function definition with yield in it"
yield
>>> type(a_function)
<class 'function'>
>>> a_generator = a_function() # when called
>>> type(a_generator) # returns a generator
<class 'generator'>
また、ジェネレーターはイテレーターです。
>>> isinstance(a_generator, collections.Iterator)
True
イテレータはイテラブルです
>>> issubclass(collections.Iterator, collections.Iterable)
True
__iter__
Iteratorを返すメソッドが必要です。
>>> collections.Iterable()
Traceback (most recent call last):
File "<pyshell#79>", line 1, in <module>
collections.Iterable()
TypeError: Can't instantiate abstract class Iterable with abstract methods __iter__
イテラブルのいくつかの例は、組み込みのタプル、リスト、辞書、セット、凍結されたセット、文字列、バイト文字列、バイト配列、範囲、およびメモリビューです。
>>> all(isinstance(element, collections.Iterable) for element in (
(), [], {}, set(), frozenset(), '', b'', bytearray(), range(0), memoryview(b'')))
True
next
か__next__
の方法をPython 2の場合:
>>> collections.Iterator()
Traceback (most recent call last):
File "<pyshell#80>", line 1, in <module>
collections.Iterator()
TypeError: Can't instantiate abstract class Iterator with abstract methods next
そしてPython 3では:
>>> collections.Iterator()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class Iterator with abstract methods __next__
次のiter
関数を使用して、組み込みオブジェクト(またはカスタムオブジェクト)からイテレータを取得できます。
>>> all(isinstance(iter(element), collections.Iterator) for element in (
(), [], {}, set(), frozenset(), '', b'', bytearray(), range(0), memoryview(b'')))
True
この__iter__
メソッドは、forループでオブジェクトを使用しようとすると呼び出されます。次に、__next__
反復子オブジェクトでメソッドを呼び出して、ループの各項目を取り出します。イテレータはStopIteration
、使い尽くしたときに発生し、その時点では再利用できません。
組み込み型ドキュメントのイテレータ型セクションのジェネレータ型セクションから:
Pythonのジェネレーターは、イテレータープロトコルを実装する便利な方法を提供します。コンテナーオブジェクトの
__iter__()
メソッドがジェネレーターとして実装されている場合、それは、__iter__()
およびnext()
[__next__()
Python 3の]メソッドを提供するイテレーターオブジェクト(技術的にはジェネレーターオブジェクト)を自動的に返します。ジェネレーターの詳細については、yield式のドキュメントを参照してください。
(強調が追加されました。)
これから、ジェネレーターは(便利な)タイプのイテレーターであることがわかります。
独自のオブジェクトを作成または拡張することにより、Iteratorプロトコルを実装するオブジェクトを作成できます。
class Yes(collections.Iterator):
def __init__(self, stop):
self.x = 0
self.stop = stop
def __iter__(self):
return self
def next(self):
if self.x < self.stop:
self.x += 1
return 'yes'
else:
# Iterators must raise when done, else considered broken
raise StopIteration
__next__ = next # Python 3 compatibility
ただし、ジェネレーターを使用してこれを行う方が簡単です。
def yes(stop):
for _ in range(stop):
yield 'yes'
あるいは、おそらくもっと単純なジェネレータ式(リスト内包表記と同様に機能):
yes_expr = ('yes' for _ in range(stop))
これらはすべて同じ方法で使用できます。
>>> stop = 4
>>> for i, y1, y2, y3 in zip(range(stop), Yes(stop), yes(stop),
('yes' for _ in range(stop))):
... print('{0}: {1} == {2} == {3}'.format(i, y1, y2, y3))
...
0: yes == yes == yes
1: yes == yes == yes
2: yes == yes == yes
3: yes == yes == yes
Pythonオブジェクトを反復可能なオブジェクトとして拡張する必要がある場合は、Iteratorプロトコルを直接使用できます。
ただし、ほとんどの場合、yield
ジェネレータイテレータを返す関数を定義するか、ジェネレータ式を検討するのに最適です。
最後に、ジェネレーターはコルーチンとしてさらに多くの機能を提供することに注意してください。「収量」キーワードは何をするのか」に対する私の答えについて、ステートメントとともにジェネレーターについてyield
詳しく説明します。
イテレータ:
イテレータは使用するオブジェクトです next()
シーケンスの次の値を取得。
発電機:
ジェネレータは、yield
メソッドを使用して一連の値を生成または生成する関数です。
ジェネレーター関数(以下の例のnext()
ex:のf
場合)から返されるジェネレーターオブジェクトのすべてのメソッド呼び出しはfoo()
、次の値を順番に生成します。
ジェネレータ関数が呼び出されると、関数の実行を開始することなくジェネレータオブジェクトを返します。場合next()
メソッドが最初に呼び出され、それが得られた値を返す収率ステートメントに達するまで、関数が実行を開始します。利回りは、つまり最後の実行を記憶することを追跡します。そして、2番目のnext()
呼び出しは前の値から続行されます。
次の例は、yieldとジェネレーターオブジェクトの次のメソッドの呼び出しとの相互作用を示しています。
>>> def foo():
... print "begin"
... for i in range(3):
... print "before yield", i
... yield i
... print "after yield", i
... print "end"
...
>>> f = foo()
>>> f.next()
begin
before yield 0 # Control is in for loop
0
>>> f.next()
after yield 0
before yield 1 # Continue for loop
1
>>> f.next()
after yield 1
before yield 2
2
>>> f.next()
after yield 2
end
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
>>>
既存の回答はいずれも公式文献の混乱を明確に扱っていないため、回答を追加します。
ジェネレータ関数は、のyield
代わりにを使用して定義された通常の関数ですreturn
。ジェネレータ関数が呼び出されると、ジェネレータオブジェクトを返します。これは、一種のイテレータであり、next()
メソッドを持っています。あなたが電話するときnext()
、ジェネレーター関数によって生成された次の値が返されます。
読み取るPythonソースドキュメントに応じて、関数またはオブジェクトは「ジェネレータ」と呼ばれる場合があります。Pythonの用語集は、一方で、発電機の機能を言うのPythonのwikiは、発電機のオブジェクトを意味します。Pythonのチュートリアルでは、非常に意味することを管理して、両方の 3文の空間での用途を:
ジェネレータは、イテレータを作成するためのシンプルで強力なツールです。これらは通常の関数のように書かれていますが、データを返したいときはいつでもyield文を使用します。next()が呼び出されるたびに、ジェネレーターは中断したところから再開します(すべてのデータ値と最後に実行されたステートメントを記憶しています)。
最初の2文はジェネレータ関数でジェネレータを識別し、3番目の文はジェネレータオブジェクトでジェネレータを識別します。
このすべての混乱にもかかわらず、明確で最終的な単語についてPython言語リファレンスを探すことができます。
収量式は、ジェネレーター関数を定義するときにのみ使用され、関数定義の本体でのみ使用できます。関数定義でyield式を使用すれば、その定義で通常の関数の代わりにジェネレーター関数を作成できます。
ジェネレータ関数が呼び出されると、ジェネレータと呼ばれるイテレータを返します。そのジェネレーターは、ジェネレーター関数の実行を制御します。
したがって、正式で正確な用法では、「修飾子」が修飾されていないということは、ジェネレーター関数ではなく、ジェネレーターオブジェクトを意味します。
上記の参照はPython 2に関するものですが、Python 3言語の参照でも同じことが言えます。ただし、Python 3の用語集では、
ジェネレータ ...通常はジェネレータ関数を指しますが、コンテキストによってはジェネレータイテレータを指す場合があります。意図した意味が明確でない場合は、完全な用語を使用することで曖昧さを回避できます。
誰もが例を挙げて本当に素晴らしく冗長な答えを持っているので、本当に感謝しています。私はまだ概念的にまだはっきりしていない人々のために短い数行の答えを出したかっただけです:
独自のイテレータを作成する場合、少し複雑になります。クラスを作成し、少なくともイテレータと次のメソッドを実装する必要があります。しかし、この手間をかけたくなくて、すぐにイテレータを作成したい場合はどうでしょう。さいわい、Pythonはイテレータを定義するためのショートカット方法を提供します。必要なのは、yieldを少なくとも1回呼び出す関数を定義することだけです。この関数を呼び出すと、「some」が返され、イテレータのように機能します(次のメソッドを呼び出してforループで使用できます)。この何かはPythonでジェネレーターと呼ばれる名前を持っています
それが少し明確になることを願っています。
以前の回答はこの追加を逃しました:ジェネレーターにはclose
メソッドがありますが、一般的なイテレーターにはありません。このclose
メソッドStopIteration
は、ジェネレーターで例外をトリガーします。これはfinally
、そのイテレーターの節でキャッチされる可能性があり、クリーンアップを実行する機会を得ます。この抽象化により、単純なイテレータよりも大きなイテレータで最も使いやすくなります。ファイルを閉じるのと同じように、下にあるものを気にすることなくジェネレータを閉じることができます。
とは言っても、最初の質問に対する私の個人的な答えは次のとおりです。反復可能には__iter__
メソッドのみがあり、一般的な反復子には__next__
メソッドのみがあり、ジェネレーターには__iter__
と、__next__
およびの両方がありclose
ます。
2番目の質問については、私の個人的な答えは次のとおりです。パブリックインターフェイスでは、弾力性があるため、ジェネレーターを非常に好む傾向があります。このclose
方法は、とのより優れた構成可能性yield from
です。ローカルでは、反復子を使用できますが、それがフラットで単純な構造であり(反復子が簡単に構成できない)、特にシーケンスが最後に到達する前に停止される可能性がある場合は、シーケンスがかなり短いと思われる理由がある場合に限ります。私はイテレータをリテラル以外の低レベルのプリミティブと見なす傾向があります。
制御フローの問題では、ジェネレーターは約束と同じくらい重要な概念です。どちらも抽象的で構成可能です。
__iter__
メソッドがあるので、イテレーターにしか持て__next__
ないのはなぜですか?それらが反復可能であると想定されている場合、私は彼らが必ず持っていることを期待します__iter__
ます。
__iter__
のみを必要とするイテレータを返すためにonイテラブルのみが必要です。標準(ダックタイピング用)とその実装(特定のPythonインタープリターによる実装)を混同しないでください。これは、ジェネレーター関数(定義)とジェネレーターオブジェクト(実装)の混乱に少し似ています。;)next
__next__
ジェネレータ関数、ジェネレータオブジェクト、ジェネレータ:
あ ジェネレータ関数は、単にPythonで通常の関数のようなものですが、それは一個の以上含まれているyield
文を。ジェネレータ関数は、イテレータオブジェクトをできるだけ簡単に作成するための優れたツールです 。イテレータジェネレータ関数によってオブジェクトreturendも呼ばれるジェネレータオブジェクトまたはジェネレータ。
この例では、Generatorオブジェクトを返すGenerator関数を作成しました<generator object fib at 0x01342480>
。他のイテレータと同じように、Generatorオブジェクトはfor
ループで使用するかnext()
、ジェネレータから次の値を返す組み込み関数で使用できます 。
def fib(max):
a, b = 0, 1
for i in range(max):
yield a
a, b = b, a + b
print(fib(10)) #<generator object fib at 0x01342480>
for i in fib(10):
print(i) # 0 1 1 2 3 5 8 13 21 34
print(next(myfib)) #0
print(next(myfib)) #1
print(next(myfib)) #1
print(next(myfib)) #2
したがって、ジェネレーター関数は、Iteratorオブジェクトを作成する最も簡単な方法です。
イテレータ:
毎 ジェネレーターオブジェクトはイテレーターですが、その逆はありません。カスタムイテレータオブジェクトは、そのクラスが実装し__iter__
、__next__
メソッド(イテレータプロトコルとも呼ばれる)を実装している場合に作成できます 。
ただし、ジェネレーター関数を使用すると、イテレーターを簡単に作成できるため、イテレーターを作成する方がはるかに簡単ですが、カスタムイテレーターを使用すると自由度が増し、次の例に示すように、要件に応じて他のメソッドを実装することもできます。
class Fib:
def __init__(self,max):
self.current=0
self.next=1
self.max=max
self.count=0
def __iter__(self):
return self
def __next__(self):
if self.count>self.max:
raise StopIteration
else:
self.current,self.next=self.next,(self.current+self.next)
self.count+=1
return self.next-self.current
def __str__(self):
return "Generator object"
itobj=Fib(4)
print(itobj) #Generator object
for i in Fib(4):
print(i) #0 1 1 2
print(next(itobj)) #0
print(next(itobj)) #1
print(next(itobj)) #1
イテレータとジェネレータに強く推奨されるNed Batchelderの例
偶数に何かを行うジェネレータのないメソッド
def evens(stream):
them = []
for n in stream:
if n % 2 == 0:
them.append(n)
return them
ジェネレーターを使って
def evens(stream):
for n in stream:
if n % 2 == 0:
yield n
return
ステートメントも必要ありませんevens
メソッド(ジェネレーター)の呼び出しは通常どおりです
num = [...]
for n in evens(num):
do_smth(n)
イテレータ
ページがいっぱいの本は反復可能、ブックマークは 反復子
このブックマークは移動以外には何もしません next
litr = iter([1,2,3])
next(litr) ## 1
next(litr) ## 2
next(litr) ## 3
next(litr) ## StopIteration (Exception) as we got end of the iterator
ジェネレータを使用するには...関数が必要です
Iteratorを使用するには...我々は必要next
とiter
言われたように:
ジェネレーター関数はイテレーターオブジェクトを返します
イテレーターの全体的な利点:
一度に1つの要素をメモリに保存する
同じデータに対して両方のアプローチを比較できます。
def myGeneratorList(n):
for i in range(n):
yield i
def myIterableList(n):
ll = n*[None]
for i in range(n):
ll[i] = i
return ll
# Same values
ll1 = myGeneratorList(10)
ll2 = myIterableList(10)
for i1, i2 in zip(ll1, ll2):
print("{} {}".format(i1, i2))
# Generator can only be read once
ll1 = myGeneratorList(10)
ll2 = myIterableList(10)
print("{} {}".format(len(list(ll1)), len(ll2)))
print("{} {}".format(len(list(ll1)), len(ll2)))
# Generator can be read several times if converted into iterable
ll1 = list(myGeneratorList(10))
ll2 = myIterableList(10)
print("{} {}".format(len(list(ll1)), len(ll2)))
print("{} {}".format(len(list(ll1)), len(ll2)))
また、メモリフットプリントを確認すると、ジェネレータはすべての値を同時にメモリに格納する必要がないため、メモリの消費量が大幅に少なくなります。
私は、Pythonの初心者向けに、非常に単純な方法で具体的に書いていますが、Pythonの深いところまでは、非常に多くのことを行います。
非常に基本的なものから始めましょう:
リストを考えて、
l = [1,2,3]
同等の関数を書きましょう:
def f():
return [1,2,3]
o / p of print(l): [1,2,3]
&o / p ofprint(f()) : [1,2,3]
リストを反復可能にしましょう:Pythonでは、リストは常に反復可能です。つまり、必要なときにいつでも反復子を適用できます。
リストにイテレータを適用しましょう:
iter_l = iter(l) # iterator applied explicitly
関数を反復可能にします。つまり、同等のジェネレーター関数を作成します。
あなたがキーワードを導入するとすぐにPythonでyield
; これはジェネレーター関数になり、反復子が暗黙的に適用されます。
注:すべてのジェネレーターは、暗黙のイテレーターが適用されていれば常に反復可能であり、ここでは暗黙のイテレーターが重要です。 したがって、ジェネレーター関数は次のようになります。
def f():
yield 1
yield 2
yield 3
iter_f = f() # which is iter(f) as iterator is already applied implicitly
したがって、関数faジェネレーターを作成するとすぐに、すでにiter(f)であることがわかります。
さて、
lはリストです。反復子メソッド「iter」を適用すると、iter(l)になります。
fはすでにiter(f)であり、反復子メソッド「iter」を適用した後、それはiter(iter(f))になり、これは再びiter(f)になります。
これは、intを既にint(x)にキャストしているため、int(x)のままになります。
たとえば、o / p of:
print(type(iter(iter(l))))
です
<class 'list_iterator'>
これはPythonであり、CまたはC ++ではないことを忘れないでください
したがって、上記の説明からの結論は次のとおりです。
リストl〜= iter(l)
ジェネレーター関数f == iter(f)