発電機の理解はどのように正確に機能しますか?


90

発電機の理解は何をしますか?それはどのように機能しますか?それに関するチュートリアルが見つかりませんでした。


1
明確にするために、これらの言語名はジェネレータの表現であり、ジェネレータの内包表記ではありません。
ShadowRanger

回答:


143

リストの内包を理解していますか?その場合、ジェネレータ式はリスト内包のようなものですが、関心のあるすべての項目を見つけてリストにパックするのではなく、待機して、式から各項目を1つずつ生成します。

>>> my_list = [1, 3, 5, 9, 2, 6]
>>> filtered_list = [item for item in my_list if item > 3]
>>> print(filtered_list)
[5, 9, 6]
>>> len(filtered_list)
3
>>> # compare to generator expression
... 
>>> filtered_gen = (item for item in my_list if item > 3)
>>> print(filtered_gen)  # notice it's a generator object
<generator object <genexpr> at 0x7f2ad75f89e0>
>>> len(filtered_gen) # So technically, it has no length
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: object of type 'generator' has no len()
>>> # We extract each item out individually. We'll do it manually first.
... 
>>> next(filtered_gen)
5
>>> next(filtered_gen)
9
>>> next(filtered_gen)
6
>>> next(filtered_gen) # Should be all out of items and give an error
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration
>>> # Yup, the generator is spent. No values for you!
... 
>>> # Let's prove it gives the same results as our list comprehension
... 
>>> filtered_gen = (item for item in my_list if item > 3)
>>> gen_to_list = list(filtered_gen)
>>> print(gen_to_list)
[5, 9, 6]
>>> filtered_list == gen_to_list
True
>>> 

ジェネレータ式は一度に1つのアイテムしか生成しないため、メモリ使用量を大幅に節約できます。ジェネレータ式は、一度に1つの項目を取得し、その項目に基づいて多くの計算を実行してから、次の項目に進む必要があるシナリオで最も意味があります。複数の値が必要な場合は、ジェネレータ式を使用して、一度にいくつかを取得することもできます。プログラムを進める前にすべての値が必要な場合は、代わりにリスト内包表記を使用してください。


3
ここで1つの質問。next(gen_name)を使用して結果を取得し、Python 3で機能しました。__next __()を使用する必要がある特定のシナリオはありますか?
Ankit Vashistha

2
@AnkitVashisthaいいえ、常に使用next(...)するのではなく、.__next__()Pythonの3に
トッド・シーウェル

@AnkitVashisthaは@gotgenes If you need more than one value, you can also use a generator expression and grab a few at a time。この使用法の例を挙げていただけますか?ありがとう。
LittleZero

19

ジェネレータ内包表記は、リスト内包表記の遅延バージョンです。

リストの代わりにイテレータを返すことを除いて、リストの内包に似ています。つまり、次の要素を生成するnext()メソッドを持つオブジェクトです。

リスト内包表記に詳しくない場合は、ここを参照してください。ジェネレータについては、こちらを参照してください


4

リスト/ジェネレーター内包は、既存のリスト/ジェネレーターから新しいリスト/ジェネレーターを作成するために使用できる構造です。

1から10までの各数値の二乗のリストを生成するとします。これはPythonで実行できます。

>>> [x**2 for x in range(1,11)]
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

ここでrange(1,11)は、リストを生成します[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]が、range関数はPython 3.0より前のジェネレータではないため、使用した構成はリスト内包です。

同じことを行うジェネレータを作成したい場合は、次のようにできます。

>>> (x**2 for x in xrange(1,11))
<generator object at 0x7f0a79273488>

ただし、Python 3ではrangeジェネレーターであるため、結果は使用する構文(角括弧または丸括弧)にのみ依存します。


4
これは間違っています。外側の式がジェネレーターであるかどうかは、内側の式がジェネレーターであるかどうかとは関係ありません。もちろん、リストから要素を取得するジェネレータ式には通常あまり意味がありませんが、それは可能です。
アンチモン

これをより明確に書き直すことはできますか?私はあなたが言っていることを理解しますが、アンチモンが言うように、あなたは別のことを言っているようです。(そして、あなたが言っているように見えることは間違っています)
リンドン・ホワイト

3

ジェネレータの理解は、特定の構造を持つジェネレータを作成する簡単な方法です。のgeneratorすべての偶数を1つずつ出力するが必要だとしますyour_list。関数スタイルを使用して作成すると、次のようになります。

def allEvens( L ):
    for number in L:
        if number % 2 is 0:
            yield number

evens = allEvens( yourList )

このジェネレーター内包式で同じ結果を得ることができます。

evens = ( number for number in your_list if number % 2 == 0 )

どちらの場合も、電話をかけるnext(evens)と、で次の偶数が返されyour_listます。


0

ジェネレータ内包は、リソース上を移動するカーソルのような、イテラブルを作成するためのアプローチです。mysqlカーソルまたはmongodbカーソルを知っている場合は、実際のデータ全体が一度にメモリに読み込まれるのではなく、一度に1つずつ読み込まれることに気付くでしょう。カーソルは前後に移動しますが、メモリ内には常に1つの行/リスト要素があります。

簡単に言うと、ジェネレータの内包表記を使用することで、Pythonで簡単にカーソルを作成できます。


-1

ジェネレーターの理解の別の例:

print 'Generator comprehensions'

def sq_num(n):
    for num in (x**2 for x in range(n)):    
        yield num

for x in sq_num(10):
    print x 
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.