1. ホーム
  2. python

[解決済み] ジェネレータ式を表示するには?

2022-06-28 11:04:05

質問

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

それ以外の方法(またはヘルパー関数を書く)で、そのジェネレータオブジェクトをインタラクティブシェルで簡単に評価・印刷することは可能でしょうか?

どのように解決するのですか?

即答です。

実行中 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()を呼び出すのと同じことです。 つまり、ジェネレータ式をリストにする方法は、それを括弧で囲むことです。

詳しい説明です。

ジェネレータ式は、"naked" for のような式です。というように。

x*x for x in range(10)

さて、これを単独で1行に貼り付けると、構文エラーになります。しかし、それを括弧で囲むことはできます。

>>> (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}

そして、Python3でのリスト内包。

>>> 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では、リスト内包は単なる構文上の糖分ではありません。しかし、唯一の違いは、Python 2の下ではxが名前空間にリークすることです。

>>> 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]