Python: Generatorausdruck drucken?

103

Wenn ich in der Python-Shell ein Listenverständnis eingebe, wie z.

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

Ich bekomme ein schön gedrucktes Ergebnis:

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

Gleiches gilt für ein Wörterbuchverständnis:

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

Wenn ich einen Generatorausdruck eingebe, erhalte ich keine so freundliche Antwort:

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

Ich weiß, ich kann dies tun:

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

Kann ich das Generatorobjekt in der interaktiven Shell einfach auswerten und ausdrucken (oder eine Hilfsfunktion schreiben)?

der Wolf
quelle
2
Was ist hier das eigentliche Problem? Was fehlt dir?
Andreas Jung
3
@pynator: Das "eigentliche Problem" ist nur, dass ich in der Lage sein möchte, den Inhalt zu drucken, generator objectwährend ich an der interaktiven Eingabeaufforderung interaktiv ein Verständnis aufbaue. Anrufen list(_)macht das. Was ich getan habe, ist, Listenverständnisse zu verwenden und diese dann in größerem Code in Genexp umzuwandeln. Diese können zur Laufzeit auf eine Weise fehlschlagen, die Listenverständnisse nicht haben.
Der Wolf
5
Die kurze Antwort lautet, dass ein Generatorausdruck nicht gedruckt werden kann, da seine Werte nicht vorhanden sind. Sie werden auf Anfrage generiert. Was Sie tun können (vorausgesetzt, der Generator stoppt irgendwann), ist, alle Werte wie mit herauszuholen list()und sie dann auszudrucken.
Kos

Antworten:

161

Schnelle Antwort:

Das Umgehen list()eines Generatorausdrucks ist (fast) genau gleichbedeutend mit []Klammern. Also ja, du kannst es tun

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

Aber Sie können es genauso gut tun

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

Ja, das macht den Generatorausdruck zu einem Listenverständnis. Es ist dasselbe und ruft list () auf. Um einen Generatorausdruck in eine Liste aufzunehmen, müssen Sie ihn in Klammern setzen.

Ausführliche Erklärung:

Ein Generatorausdruck ist ein "nackter" forAusdruck. Wie so:

x*x for x in range(10)

Jetzt können Sie das nicht alleine in eine Zeile kleben, es wird ein Syntaxfehler angezeigt. Aber Sie können es in Klammern setzen.

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

Dies wird manchmal als Generatorverständnis bezeichnet, obwohl ich denke, dass der offizielle Name immer noch Generatorausdruck ist, gibt es keinen wirklichen Unterschied, die Klammern dienen nur dazu, die Syntax gültig zu machen. Sie benötigen sie nicht, wenn Sie sie als einzigen Parameter an eine Funktion übergeben, zum Beispiel:

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

Grundsätzlich sind alle anderen in Python 3 und Python 2.7 verfügbaren Verständnisse nur syntaktischer Zucker um einen Generatorausdruck. Verständnis festlegen:

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

Diktatverständnis:

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

Und Listenverständnisse unter 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]

Unter Python 2 ist Listenverständnis nicht nur syntaktischer Zucker. Der einzige Unterschied ist jedoch, dass x unter Python 2 in den Namespace gelangt.

>>> x
9

Unter Python 3 erhalten Sie

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

Dies bedeutet, dass der beste Weg, um einen schönen Ausdruck des Inhalts Ihres Generatorausdrucks in Python zu erhalten, darin besteht, daraus ein Listenverständnis zu machen! Dies funktioniert jedoch offensichtlich nicht, wenn Sie bereits ein Generatorobjekt haben. Dadurch wird nur eine Liste mit einem Generator erstellt:

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

In diesem Fall müssen Sie anrufen list():

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

Das funktioniert zwar, ist aber irgendwie dumm:

>>> [x for x in foo]
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
Lennart Regebro
quelle
5
Der offizielle Begriff bleibt "Generatorausdruck", weil das Wort "Verständnis" Iteration impliziert, was eine Sache ist, die ein Genexp nicht tut, wie diese Frage und Antwort gut illustrieren :)
ncoghlan
2
list( generator-expression )druckt nicht den Generatorausdruck; Es wird eine Liste erstellt (und dann in einer interaktiven Shell gedruckt). Anstatt eine Liste zu generieren, können Sie in Python 3 den Generatorausdruck in eine print-Anweisung aufteilen. Dh) print(*(generator-expression)). Dadurch werden die Elemente am Anfang und am Ende ohne Kommas und ohne Klammern gedruckt.
AJNeufeld
17

Im Gegensatz zu einer Liste oder einem Wörterbuch kann ein Generator unendlich sein. Dies würde nicht funktionieren:

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

Wenn Sie einen Generator lesen, wird er geändert, sodass es keine perfekte Möglichkeit gibt, ihn anzuzeigen. Sie können ein Beispiel für die Leistung des Generators anzeigen

g1 = gen()
[g1.next() for i in range(10)]
Tschad
quelle
2
Abgestimmt aufgrund der Aussage, dass ein Generator unendlich sein kann und daher eine Schleife oder einen Totalstopp verursacht (abhängig von Ihren Spezifikationen (lol)).
Milan Velebit
16

Sie können den Ausdruck einfach in einen Aufruf einbinden 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
quelle
15

Oder Sie können jederzeit mapeinen Iterator verwenden, ohne eine Zwischenliste erstellen zu müssen:

>>> _ = map(sys.stdout.write, (x for x in string.letters if x in (y for y in "BigMan on campus")))
acgimnopsuBM
lbolla
quelle
3
Dies ist die einzige Antwort, die tatsächlich den Inhalt des Generators druckt, ohne ein großes Objekt zu erstellen.
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
quelle
Wenn der Generator unendlich ist, verursacht er eine Schleife.
Milan Velebit