Der wichtige Punkt ist, dass das Listenverständnis eine neue Liste erstellt. Der Generator erstellt ein iterierbares Objekt, das das Quellmaterial im laufenden Betrieb "filtert", während Sie die Bits verbrauchen.
Stellen Sie sich vor, Sie haben eine 2-TB-Protokolldatei mit dem Namen "vastfile.txt" und möchten den Inhalt und die Länge aller Zeilen, die mit dem Wort "ENTRY" beginnen.
Versuchen Sie also zunächst, ein Listenverständnis zu schreiben:
logfile = open("hugefile.txt","r")
entry_lines = [(line,len(line)) for line in logfile if line.startswith("ENTRY")]
Dadurch wird die gesamte Datei verschluckt, jede Zeile verarbeitet und die übereinstimmenden Zeilen in Ihrem Array gespeichert. Dieses Array kann daher bis zu 2 TB Inhalt enthalten. Das ist viel RAM und wahrscheinlich nicht praktisch für Ihre Zwecke.
Stattdessen können wir einen Generator verwenden, um einen "Filter" auf unseren Inhalt anzuwenden. Es werden tatsächlich keine Daten gelesen, bis wir beginnen, über das Ergebnis zu iterieren.
logfile = open("hugefile.txt","r")
entry_lines = ((line,len(line)) for line in logfile if line.startswith("ENTRY"))
Es wurde noch nicht einmal eine einzige Zeile aus unserer Datei gelesen. Nehmen wir an, wir möchten unser Ergebnis noch weiter filtern:
long_entries = ((line,length) for (line,length) in entry_lines if length > 80)
Es wurde noch nichts gelesen, aber wir haben jetzt zwei Generatoren angegeben, die auf unsere Daten reagieren, wie wir es wünschen.
Schreiben wir unsere gefilterten Zeilen in eine andere Datei:
outfile = open("filtered.txt","a")
for entry,length in long_entries:
outfile.write(entry)
Jetzt lesen wir die Eingabedatei. Da unsere for
Schleife weiterhin zusätzliche Zeilen anfordert, long_entries
fordert der Generator Zeilen vom entry_lines
Generator an und gibt nur diejenigen zurück, deren Länge größer als 80 Zeichen ist. Der entry_lines
Generator fordert wiederum Zeilen (gefiltert wie angegeben) vom logfile
Iterator an, der wiederum die Datei liest.
Anstatt Daten in Form einer vollständig ausgefüllten Liste an Ihre Ausgabefunktion zu "pushen", geben Sie der Ausgabefunktion die Möglichkeit, Daten nur dann zu "ziehen", wenn sie benötigt werden. Dies ist in unserem Fall viel effizienter, aber nicht ganz so flexibel. Generatoren sind eine Möglichkeit, ein Durchgang; Die Daten aus der von uns gelesenen Protokolldatei werden sofort verworfen, sodass wir nicht zu einer vorherigen Zeile zurückkehren können. Auf der anderen Seite müssen wir uns keine Sorgen mehr machen, dass die Daten erhalten bleiben, wenn wir damit fertig sind.
[exp for x in iter]
nur Zucker seinlist((exp for x in iter))
? oder gibt es einen Ausführungsunterschied?