Python: bir oluşturucu ifadesi yazdırmak?


103

Python kabuğunda, aşağıdaki gibi bir liste anlayışı girersem:

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

Güzel yazdırılmış bir sonuç alıyorum:

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

Aynı sözlük anlayışı için de:

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

Bir üreteç ifadesi girersem, o kadar samimi bir yanıt alamıyorum:

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

Bunu yapabileceğimi biliyorum:

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

Bunun dışında (veya bir yardımcı işlev yazarken), bu jeneratör nesnesini etkileşimli kabukta kolayca değerlendirebilir ve yazdırabilir miyim?


2
Buradaki asıl sorun nedir? Neyi özlüyorsun?
Andreas Jung

3
@pynator: "Gerçek sorun" sadece generator objectetkileşimli komut isteminde etkileşimli olarak bir anlayış oluştururken içeriğini yazdırabilmek istemem . Aramak list(_)bunu yapar. Yaptığım şey liste anlamalarını kullanmak ve daha sonra bunları daha büyük kodda genexp'e dönüştürmek. Bunlar çalışma zamanında anlamaların listelemediği şekilde başarısız olabilir.
kurt

5
Kısa cevap, bir üretici ifadesinin, değerleri olmadığı için yazdırılamayacağıdır; talep üzerine üretilirler. Yapabileceğiniz şey (jeneratörün bazen durduğunu varsayarsak) ile olduğu gibi ondan tüm değerleri almak list()ve sonra bunları yazdırmaktır.
Kos

Yanıtlar:


161

Hızlı cevap:

list()Bir üreteç ifadesinin etrafında yapmak , (neredeyse) tam olarak []etrafında köşeli parantezlerin olmasına eşdeğerdir . Yani evet yapabilirsin

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

Ama sen de yapabilirsin

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

Evet, bu jeneratör ifadesini bir liste anlayışına dönüştürecektir. Aynı şey ve üzerinde arama listesi (). Bu nedenle, bir üreteç ifadesini bir listeye yapmanın yolu, etrafına parantez koymaktır.

Detaylı açıklama:

Oluşturucu ifade "çıplak" bir forifadedir. Şöyle:

x*x for x in range(10)

Şimdi, bunu tek başına bir satıra yapıştıramazsınız, sözdizimi hatası alırsınız. Ancak etrafına parantez koyabilirsiniz.

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

Bu bazen bir üreteç anlayışı olarak adlandırılır, ancak resmi ismin hala üreteç ifadesi olduğunu düşünüyorum, gerçekten bir fark yok, parantezler sadece sözdizimini geçerli kılmak için var. Bir işleve tek parametre olarak aktarıyorsanız bunlara ihtiyacınız yoktur, örneğin:

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

Temelde Python 3 ve Python 2.7'de bulunan diğer tüm anlayışlar, bir üreteç ifadesinin etrafındaki sözdizimsel şekerdir. Anlayışları ayarlayın:

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

Dikte anlayışları:

>>> 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 altında anlamaları listeleyin:

>>> 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'ye göre, liste anlayışları sadece sözdizimsel şeker değildir. Ancak tek fark, x'in Python 2 altında ad alanına sızmasıdır.

>>> x
9

Python 3 altındayken

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

Bu, Python'da oluşturucu ifadenizin içeriğinin güzel bir çıktısını almanın en iyi yolunun, bir liste kavrayışı yapmak olduğu anlamına gelir! Ancak, zaten bir jeneratör nesneniz varsa, bu kesinlikle işe yaramayacaktır. Bunu yapmak sadece bir jeneratörün bir listesini yapacaktır:

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

Bu durumda aramanız gerekecek list():

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

Bu işe yarasa da, ama biraz aptalca:

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

5
Resmi terim "üreteç ifadesi" olarak kalır çünkü "anlama" kelimesi yinelemeyi ima eder, ki bu bir genexp'in yapmadığı bir şeydir , çünkü bu soru ve cevap güzelce göstermektedir :)
ncoghlan

2
list( generator-expression )oluşturucu ifadesini yazdırmıyor; bir liste oluşturuyor (ve sonra onu etkileşimli bir kabukta yazdırıyor). Python 3'te bir liste oluşturmak yerine, jeneratör ifadesini bir print ifadesine dönüştürebilirsiniz. Yani) print(*(generator-expression)). Bu, öğeleri virgül olmadan ve başında ve sonunda köşeli parantez olmadan yazdırır.
AJNeufeld

18

Bir liste veya sözlüğün aksine, bir oluşturucu sonsuz olabilir. Bunu yapmak işe yaramaz:

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

Ayrıca, bir jeneratör okumak onu değiştirir, bu yüzden onu görüntülemenin mükemmel bir yolu yoktur. Üreticinin çıktısının bir örneğini görmek için şunları yapabilirsiniz:

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

2
Bir jeneratörün sonsuz olabileceği ifadesi nedeniyle oy verildi, bu nedenle bir döngüye veya toplam durmaya neden oluyor (özelliklerinize (lol) bağlı olarak).
Milan Velebit

[next(g1) for i in range(10)]Python 3'te kullanın
Deepank

16

İfadeyi bir çağrıya sarabilirsiniz 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

Veya mapbir ara liste oluşturmanıza gerek kalmadan her zaman bir yineleyicinin üzerinden geçebilirsiniz :

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

3
bu, gerçekten büyük bir nesne oluşturmadan jeneratörün içeriğini yazdıran tek cevaptır.
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']

Jeneratörün sonsuz olması durumunda, bir döngüye neden olur.
Milan Velebit
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.