Python: wydrukować wyrażenie generatora?

103

W powłoce Pythona, jeśli wprowadzę zrozumienie listy, takie jak:

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

Otrzymuję ładnie wydrukowany wynik:

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

To samo dotyczy rozumienia ze słownika:

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

Jeśli wpisuję wyrażenie generatora, nie otrzymuję tak przyjaznej odpowiedzi:

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

Wiem, że potrafię to zrobić:

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

Poza tym (lub pisząc funkcję pomocniczą) czy mogę łatwo ocenić i wydrukować ten obiekt generatora w powłoce interaktywnej?

Wilk
źródło
2
Jaki jest prawdziwy problem? Co ci umyka?
Andreas Jung,
3
@pynator: „Prawdziwym problemem” jest po prostu to, że chcę móc wydrukować treść, generator objectgdy interaktywnie buduję zrozumienie w interaktywnym monicie. Dzwonienie to list(_)robi. To, co zrobiłem, to użycie list złożonych, a następnie przekształcenie ich w genexp w większym kodzie. Mogą one zakończyć się niepowodzeniem w czasie wykonywania w sposób, w jaki nie ma to ze składanymi listami.
wilk
5
Krótka odpowiedź brzmi: wyrażenie generatora nie może zostać wydrukowane, ponieważ jego wartości nie istnieją; są generowane na żądanie. To, co możesz zrobić (zakładając, że generator kiedyś się zatrzyma), to wyciągnąć z niego wszystkie wartości, na przykład za pomocą list(), a następnie je wydrukować.
Kos

Odpowiedzi:

161

Szybka odpowiedź:

Obchodzenie list()się z wyrażeniem generatora jest (prawie) dokładnie równoważne z []otaczaniem go nawiasami. Więc tak, możesz to zrobić

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

Ale równie dobrze możesz to zrobić

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

Tak, to zmieni wyrażenie generatora w rozumienie listy. To to samo i lista wywołań () na nim. Tak więc sposobem na przekształcenie wyrażenia generatora w listę jest umieszczenie wokół niego nawiasów.

Szczegółowe wyjaśnienie:

Wyrażenie generatora to wyrażenie „nagie” for. Tak jak to:

x*x for x in range(10)

Teraz nie możesz umieścić tego samego w linii, otrzymasz błąd składni. Ale możesz umieścić to w nawiasach.

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

Nazywa się to czasem zrozumieniem generatora, chociaż myślę, że oficjalna nazwa nadal jest wyrażeniem generatora, tak naprawdę nie ma żadnej różnicy, nawiasy są tylko po to, aby składnia była poprawna. Nie potrzebujesz ich, jeśli przekazujesz je jako jedyny parametr do funkcji, na przykład:

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

Zasadniczo wszystkie inne wyrażenia dostępne w Pythonie 3 i Pythonie 2.7 to po prostu cukier syntaktyczny wokół wyrażenia generatora. Zestaw pojęć:

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

Rozumienie ze zrozumieniem:

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

I listy składanych w Pythonie 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]

W Pythonie 2 listy składane to nie tylko cukier składniowy. Ale jedyna różnica polega na tym, że x pod Pythonem 2 przecieka do przestrzeni nazw.

>>> x
9

Będąc pod Pythonem 3, otrzymasz

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

Oznacza to, że najlepszym sposobem na uzyskanie ładnego wydruku zawartości wyrażenia generatora w Pythonie jest zrobienie z niego zrozumienia listy! Jednak to oczywiście nie zadziała, jeśli masz już obiekt generatora. Spowoduje to utworzenie listy jednego generatora:

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

W takim przypadku będziesz musiał zadzwonić list():

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

Chociaż to działa, ale jest trochę głupie:

>>> [x for x in foo]
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
Lennart Regebro
źródło
5
Oficjalnym terminem pozostaje „wyrażenie generujące”, ponieważ słowo „zrozumienie” implikuje iterację, czego nie robi genexp , ponieważ to pytanie i odpowiedź ładnie ilustrują :)
ncoghlan
2
list( generator-expression )nie drukuje wyrażenia generatora; generuje listę (a następnie drukuje ją w interaktywnej powłoce). Zamiast generować listę, w Pythonie 3 można zamieścić wyrażenie generatora na instrukcję print. Tj print(*(generator-expression)). ) . Wypisuje elementy bez przecinków i bez nawiasów na początku i na końcu.
AJNeufeld
18

W przeciwieństwie do listy lub słownika, generator może być nieskończony. To nie zadziała:

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

Ponadto czytanie generatora zmienia to, więc nie ma idealnego sposobu, aby go wyświetlić. Aby zobaczyć próbkę wyjścia generatora, możesz to zrobić

g1 = gen()
[g1.next() for i in range(10)]
czad
źródło
2
Głosowano z powodu stwierdzenia, że ​​generator może być nieskończony, powodując pętlę lub całkowite zatrzymanie (w zależności od specyfikacji (lol)).
Milan Velebit
Użyj [next(g1) for i in range(10)]w Pythonie 3.
Deepank
16

Możesz po prostu zawinąć wyrażenie w wywołanie 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
źródło
15

Lub zawsze możesz mapnad iteratorem, bez potrzeby tworzenia listy pośredniej:

>>> _ = map(sys.stdout.write, (x for x in string.letters if x in (y for y in "BigMan on campus")))
acgimnopsuBM
lbolla
źródło
3
jest to jedyna odpowiedź, która faktycznie drukuje zawartość generatora bez tworzenia dużego obiektu.
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
źródło
W przypadku, gdy generator jest nieskończony, spowoduje pętlę.
Milan Velebit