Dies ist eher die Umkehrung von Wofür können Sie Python-Generatorfunktionen verwenden? : Python-Generatoren, Generatorausdrücke und das itertools
Modul sind einige meiner Lieblingsfunktionen von Python in diesen Tagen. Sie sind besonders nützlich, wenn Sie Betriebsketten einrichten, die für einen großen Datenstapel ausgeführt werden sollen. Ich verwende sie häufig bei der Verarbeitung von DSV-Dateien.
Wann ist es also kein guter Zeitpunkt, einen Generator, einen Generatorausdruck oder eine itertools
Funktion zu verwenden?
- Wann sollte ich lieber
zip()
alsitertools.izip()
oder range()
vorbeixrange()
oder[x for x in foo]
vorbei(x for x in foo)
?
Offensichtlich müssen wir irgendwann einen Generator in tatsächliche Daten "auflösen", normalerweise indem wir eine Liste erstellen oder mit einer Nicht-Generator-Schleife darüber iterieren. Manchmal müssen wir nur die Länge kennen. Das frage ich nicht.
Wir verwenden Generatoren, damit wir keine neuen Listen für Zwischendaten im Speicher zuweisen. Dies ist insbesondere bei großen Datenmengen sinnvoll. Ist es auch für kleine Datensätze sinnvoll? Gibt es einen spürbaren Kompromiss zwischen Speicher und CPU?
Ich bin besonders interessiert, wenn jemand ein Profil erstellt hat, angesichts der aufschlussreichen Diskussion über die Leistung des Listenverständnisses im Vergleich zu map () und filter () . ( Alt Link )
quelle
<5
.Antworten:
Verwenden Sie eine Liste anstelle eines Generators, wenn:
1) Sie müssen die Daten für den Zugriff auf mehrere Male (dh Cache die Ergebnisse statt recomputing sie):
2) Sie benötigen einen Direktzugriff (oder einen anderen Zugriff als die sequentielle Vorwärtsreihenfolge):
3) Sie müssen sich verbinden Strings (die zwei Durchgänge über die Daten benötigt):
4) Sie verwenden PyPy, das den Generatorcode manchmal nicht so stark optimieren kann wie bei normalen Funktionsaufrufen und Listenmanipulationen.
quelle
ireduce
der Join repliziert wird?''.join('%s' % i for i in xrange(10))
Verwenden Sie im Allgemeinen keinen Generator, wenn Sie Listenoperationen wie len (), reverse () usw. benötigen.
Es kann auch vorkommen, dass Sie keine verzögerte Auswertung wünschen (z. B. die gesamte Berechnung im Voraus durchführen, damit Sie eine Ressource freigeben können). In diesem Fall ist ein Listenausdruck möglicherweise besser.
quelle
Profil, Profil, Profil.
Das Profilieren Ihres Codes ist der einzige Weg, um festzustellen, ob das, was Sie tun, überhaupt Auswirkungen hat.
Die meisten Verwendungen von xrange, Generatoren usw. haben eine statische Größe und kleine Datensätze. Nur wenn Sie zu großen Datenmengen gelangen, macht dies wirklich einen Unterschied. Bei range () vs. xrange () geht es meistens nur darum, den Code ein bisschen hässlicher aussehen zu lassen, nichts zu verlieren und vielleicht etwas zu gewinnen.
Profil, Profil, Profil.
quelle
Sie sollten sich nicht begünstigen
zip
überizip
,range
überxrange
oder Listenkomprehensionen über Generator Comprehensions. In Python 3.0range
hatxrange
-ähnliche Semantik undzip
hatizip
-ähnliche Semantik.Das Listenverständnis ist tatsächlich klarer, als
list(frob(x) for x in foo)
wenn Sie eine tatsächliche Liste benötigen.quelle
for
Schleifen!), Aber man kann leicht unverständliche Listenverständnisse schreiben.[]
Form beschreibend genug (und prägnanter und im Allgemeinen weniger überladen). Dies ist jedoch nur eine Frage des Geschmacks.Wie Sie bereits erwähnt haben: "Dies ist besonders bei großen Datenmengen sinnvoll", beantwortet dies Ihrer Meinung nach Ihre Frage.
Wenn Sie in Bezug auf die Leistung keine Wände erreichen, können Sie sich dennoch an Listen und Standardfunktionen halten. Wenn Sie dann auf Leistungsprobleme stoßen, wechseln Sie.
Wie von @ u0b34a0f6ae in den Kommentaren erwähnt, kann die Verwendung von Generatoren zu Beginn die Skalierung auf größere Datensätze erleichtern.
quelle
In Bezug auf die Leistung: Wenn Sie Psyco verwenden, können Listen viel schneller sein als Generatoren. Im folgenden Beispiel sind Listen bei Verwendung von psyco.full () fast 50% schneller.
Ergebnisse:
quelle
Was die Leistung betrifft, kann ich mir keine Zeiten vorstellen, in denen Sie eine Liste über einen Generator verwenden möchten.
quelle
all(True for _ in range(10 ** 8))
ist langsamer alsall([True for _ in range(10 ** 8)])
in Python 3.8. Ich würde hier eine Liste einem Generator vorziehenIch habe noch nie eine Situation gefunden, in der Generatoren Ihre Versuche behindern würden. Es gibt jedoch viele Fälle, in denen die Verwendung von Generatoren Ihnen nicht mehr helfen würde, als sie nicht zu verwenden.
Beispielsweise:
Bietet keine Verbesserung gegenüber:
quelle
range(5)
, da die resultierende Liste bereits sortiert ist.Sie sollten Listenverständnisse bevorzugen, wenn Sie die Werte später für etwas anderes behalten müssen und die Größe Ihres Sets nicht zu groß ist.
Beispiel: Sie erstellen eine Liste, die Sie später in Ihrem Programm mehrmals durchlaufen werden.
In gewissem Maße können Sie sich Generatoren als Ersatz für Iteration (Schleifen) und Listenverständnis als eine Art Datenstrukturinitialisierung vorstellen. Wenn Sie die Datenstruktur beibehalten möchten, verwenden Sie Listenverständnisse.
quelle
itertools.tee()
kann Ihnen dies möglicherweise helfen. Wenn Sie jedoch mehr als einen Durchgang oder zufälligen Zugriff auf einige Zwischendaten wünschen, erstellen Sie im Allgemeinen eine Liste / einen Satz / ein Diktat daraus.