Python:ジェネレータ式を出力しますか?


103

Pythonシェルで、次のようなリスト内包表記を入力すると、

>>> [x for x in string.letters if x in [y for y in "BigMan on campus"]]

うまく印刷された結果が得られます。

['a', 'c', 'g', 'i', 'm', 'n', 'o', 'p', 's', 'u', 'B', 'M']

辞書内包についても同様です。

>>> {x:x*2 for x in range(1,10)}
{1: 2, 2: 4, 3: 6, 4: 8, 5: 10, 6: 12, 7: 14, 8: 16, 9: 18}

ジェネレータ式を入力すると、それほど親切な応答が得られません。

>>> (x for x in string.letters if x in (y for y in "BigMan on campus"))
<generator object <genexpr> at 0x1004a0be0>

私はこれができることを知っています:

>>> for i in _: print i,
a c g i m n o p s u B M

それ以外の場合(またはヘルパー関数を作成する場合)、そのジェネレーターオブジェクトを対話型シェルで簡単に評価して印刷できますか?


2
ここで本当の問題は何ですか?何が欠けていますか?
Andreas Jung

3
@pynator:「本当の問題」はgenerator object、インタラクティブプロンプトで理解をインタラクティブに構築するときに、その内容を印刷できるようにすることです。呼び出しlist(_)はそれを行います。私がやったことは、リスト内包表記を使用してから、それらをより大きなコードでgenexpに変換することです。これらは、リスト内包表記がそうでない方法で実行時に失敗する可能性があります。
オオカミ

5
つまり、値が存在しないため、ジェネレータ式は出力できません。オンデマンドで生成されます。あなたができること(ジェネレータがいつか停止すると仮定すると)は、のようにlist()、そこからすべての値を取得して、それらを出力することです。
コス

回答:


161

素早い回答:

実行list()ジェネレータ式の周りには(ほとんど)を有すると正確に等価である []その周りブラケット。そう、そうすることができます

>>> list((x for x in string.letters if x in (y for y in "BigMan on campus")))

しかし、あなたも同様に行うことができます

>>> [x for x in string.letters if x in (y for y in "BigMan on campus")]

はい、それはジェネレータ式をリスト内包に変えます。それはlist()を呼び出すのと同じことです。したがって、ジェネレータ式をリストにする方法は、括弧で囲むことです。

詳細な説明:

ジェネレータ式は「ネイキッド」for式です。そのようです:

x*x for x in range(10)

さて、それだけでは行に固定できないため、構文エラーが発生します。ただし、括弧を付けることができます。

>>> (x*x for x in range(10))
<generator object <genexpr> at 0xb7485464>

これはジェネレーター内包表記と呼ばれることもありますが、正式名称はまだジェネレーター式であると思いますが、実際には違いはありません。括弧は構文を有効にするためだけにあります。たとえば、関数への唯一のパラメーターとして渡す場合は必要ありません。

>>> sorted(x*x for x in range(10))
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

基本的に、Python 3とPython 2.7で利用可能な他のすべての内包表記は、ジェネレーター式の構文糖衣です。理解度を設定する:

>>> {x*x for x in range(10)}
{0, 1, 4, 81, 64, 9, 16, 49, 25, 36}

>>> set(x*x for x in range(10))
{0, 1, 4, 81, 64, 9, 16, 49, 25, 36}

口語の理解:

>>> dict((x, x*x) for x in range(10))
{0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36, 7: 49, 8: 64, 9: 81}

>>> {x: x*x for x in range(10)}
{0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36, 7: 49, 8: 64, 9: 81}

また、Python 3で理解度をリストします。

>>> list(x*x for x in range(10))
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

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

Python 2では、リスト内包表記は単に構文上の砂糖ではありません。しかし、唯一の違いは、xがPython 2で名前空間にリークすることです。

>>> x
9

Python 3の下では、

>>> x
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'x' is not defined

これは、Pythonでジェネレータ式のコンテンツを適切に出力するための最良の方法は、リストの内包を理解することであることを意味します。ただし、ジェネレーターオブジェクトが既にある場合、これは明らかに機能しません。これを行うと、1つのジェネレータのリストが作成されます。

>>> foo = (x*x for x in range(10))
>>> [foo]
[<generator object <genexpr> at 0xb7559504>]

その場合は、以下を呼び出す必要がありますlist()

>>> list(foo)
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

これは機能しますが、少し愚かですが:

>>> [x for x in foo]
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

5
公式の用語が残るgenexpが一つである「ジェネレータ式」単語「理解」は繰り返しを意味するので、しないこの質問と回答などを行うには、:)うまく説明
ncoghlan

2
list( generator-expression )ジェネレータ式を出力していません。リストを生成しています(インタラクティブシェルで印刷しています)。リストを生成する代わりに、Python 3では、ジェネレーター式を印刷ステートメントにスプラットできます。すなわち)print(*(generator-expression))。これにより、要素の最初と最後にカンマと角括弧なしで出力されます。
AJNeufeld

17

リストや辞書とは異なり、ジェネレータは無限にできます。これを行ってもうまくいきません:

def gen():
    x = 0
    while True:
        yield x
        x += 1
g1 = gen()
list(g1)   # never ends

また、ジェネレーターを読み取るとそれが変更されるため、ジェネレーターを表示する完璧な方法はありません。ジェネレーターの出力のサンプルを表示するには、次のようにします

g1 = gen()
[g1.next() for i in range(10)]

2
ジェネレーターは無限である可能性があり、したがってループまたは完全な停止を引き起こす(仕様に応じて(笑))との声明により、賛成票が投じられました。
Milan Velebit 2017年

16

次の呼び出しで式をラップするだけですlist

>>> list(x for x in string.letters if x in (y for y in "BigMan on campus"))
['a', 'c', 'g', 'i', 'm', 'n', 'o', 'p', 's', 'u', 'B', 'M']

15

またはmap、中間リストを作成する必要なしに、常にイテレータを超えることができます。

>>> _ = map(sys.stdout.write, (x for x in string.letters if x in (y for y in "BigMan on campus")))
acgimnopsuBM

3
これは、巨大なオブジェクトを作成せずにジェネレーターの内容を実際に出力する唯一の答えです。
Marek R

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