Python: mencetak ekspresi generator?

103

Di shell Python, jika saya memasukkan pemahaman daftar seperti:

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

Saya mendapatkan hasil cetakan yang bagus:

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

Sama untuk pemahaman kamus:

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

Jika saya memasukkan ekspresi generator, saya tidak mendapatkan respons yang ramah:

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

Saya tahu saya bisa melakukan ini:

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

Selain itu (atau menulis fungsi pembantu) dapatkah saya dengan mudah mengevaluasi dan mencetak objek generator itu di shell interaktif?

serigala
sumber
2
Apa masalah sebenarnya di sini? Apa yang kamu lewatkan?
Andreas Jung
3
@pynator: "Masalah sebenarnya" adalah saya hanya ingin dapat mencetak konten generator objectsaat saya secara interaktif membangun pemahaman pada prompt interaktif. Menelepon list(_)memang begitu. Apa yang telah saya lakukan adalah menggunakan pemahaman daftar kemudian mengubahnya menjadi genexp dalam kode yang lebih besar. Ini bisa gagal pada waktu berjalan dengan cara yang tidak dilakukan oleh pemahaman daftar.
serigala
5
Jawaban singkatnya adalah ekspresi generator tidak dapat dicetak karena nilainya tidak ada; mereka dibuat sesuai permintaan. Apa yang dapat Anda lakukan (dengan asumsi generator berhenti sewaktu-waktu) adalah mendapatkan semua nilai darinya, seperti dengan list(), dan kemudian mencetaknya.
Kos

Jawaban:

161

Jawaban cepat:

Melakukan di list()sekitar ekspresi generator (hampir) persis sama dengan memiliki []tanda kurung di sekitarnya. Jadi ya, Anda bisa melakukannya

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

Tapi Anda juga bisa melakukannya

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

Ya, itu akan mengubah ekspresi generator menjadi pemahaman daftar. Itu hal yang sama dan daftar panggilan () di atasnya. Jadi cara membuat ekspresi generator menjadi daftar adalah dengan menempatkan tanda kurung di sekitarnya.

Penjelasan detail:

Ekspresi generator adalah ekspresi "telanjang" for. Seperti:

x*x for x in range(10)

Sekarang, Anda tidak dapat menempelkannya pada satu baris dengan sendirinya, Anda akan mendapatkan kesalahan sintaks. Tapi Anda bisa meletakkan tanda kurung di sekitarnya.

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

Ini kadang-kadang disebut pemahaman generator, meskipun saya pikir nama resminya masih ekspresi generator, sebenarnya tidak ada perbedaan apa pun, tanda kurung hanya ada di sana untuk membuat sintaksnya valid. Anda tidak membutuhkannya jika Anda meneruskannya sebagai satu-satunya parameter ke suatu fungsi misalnya:

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

Pada dasarnya semua pemahaman lain yang tersedia di Python 3 dan Python 2.7 hanyalah gula sintaksis di sekitar ekspresi generator. Set pemahaman:

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

Pemahaman Dict:

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

Dan daftar pemahaman di bawah 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]

Di bawah Python 2, pemahaman daftar bukan hanya gula sintaksis. Tetapi satu-satunya perbedaan adalah bahwa x akan di bawah Python 2 bocor ke namespace.

>>> x
9

Sementara di bawah Python 3 Anda akan mendapatkan

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

Ini berarti bahwa cara terbaik untuk mendapatkan cetakan yang bagus dari konten ekspresi generator Anda dengan Python adalah dengan membuat pemahaman daftar darinya! Namun, ini jelas tidak akan berhasil jika Anda sudah memiliki objek generator. Melakukan itu hanya akan membuat daftar satu generator:

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

Dalam hal ini, Anda perlu menelepon list():

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

Meskipun ini berhasil, tetapi agak bodoh:

>>> [x for x in foo]
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
Lennart Regebro
sumber
5
Istilah resmi tetap "ekspresi generator" karena kata "pemahaman" menyiratkan iterasi, yang merupakan satu hal yang tidak dilakukan oleh genexp , karena pertanyaan dan jawaban ini menggambarkan dengan baik :)
ncoghlan
2
list( generator-expression )tidak mencetak ekspresi generator; itu membuat daftar (dan kemudian mencetaknya dalam shell interaktif). Alih-alih membuat daftar, dengan Python 3, Anda dapat membagi ekspresi generator menjadi pernyataan cetak. Yaitu) print(*(generator-expression)). Ini mencetak elemen tanpa koma dan tanpa tanda kurung di awal dan akhir.
AJNeufeld
18

Tidak seperti daftar atau kamus, generator bisa jadi tidak terbatas. Melakukan ini tidak akan berhasil:

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

Selain itu, membaca generator mengubahnya, jadi tidak ada cara sempurna untuk melihatnya. Untuk melihat contoh keluaran generator bisa Anda lakukan

g1 = gen()
[g1.next() for i in range(10)]
chad
sumber
2
Dipilih karena pernyataan bahwa generator bisa tidak terbatas, oleh karena itu menyebabkan loop atau penghentian total (tergantung pada spesifikasi Anda (lol)).
Milan Velebit
Gunakan [next(g1) for i in range(10)]dengan Python 3.
Deepank
16

Anda bisa membungkus ekspresi dalam panggilan ke 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']
Björn Pollex
sumber
15

Atau Anda selalu dapat mapmelalui iterator, tanpa perlu membuat daftar perantara:

>>> _ = map(sys.stdout.write, (x for x in string.letters if x in (y for y in "BigMan on campus")))
acgimnopsuBM
lbolla.dll
sumber
3
ini adalah satu-satunya jawaban yang benar-benar mencetak isi generator tanpa membuat objek besar.
Marek R
2
>>> 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']
Andreas Jung
sumber
Jika generator tidak terbatas, itu akan menyebabkan loop.
Milan Velebit