Ich habe ein Generatorobjekt, das mit mehreren Erträgen zurückgegeben wird. Die Vorbereitung zum Aufrufen dieses Generators ist ziemlich zeitaufwändig. Deshalb möchte ich den Generator mehrmals wiederverwenden.
y = FunctionWithYield()
for x in y: print(x)
#here must be something to reset 'y'
for x in y: print(x)
Natürlich denke ich daran, Inhalte in eine einfache Liste zu kopieren. Gibt es eine Möglichkeit, meinen Generator zurückzusetzen?
y = list(y)
mit dem Rest Ihres Codes unverändert verwenden.Generatoren können nicht zurückgespult werden. Sie haben folgende Möglichkeiten:
Führen Sie die Generatorfunktion erneut aus und starten Sie die Generierung neu:
Das Speichern des Generators führt zu einer Datenstruktur auf dem Speicher oder der Festplatte, die Sie erneut wiederholen können:
Der Nachteil von Option 1 ist, dass die Werte erneut berechnet werden. Wenn das CPU-intensiv ist, berechnen Sie am Ende zweimal. Auf der anderen Seite ist der Nachteil von 2 der Speicher. Die gesamte Werteliste wird gespeichert. Wenn es zu viele Werte gibt, kann dies unpraktisch sein.
Sie haben also den klassischen Kompromiss zwischen Speicher und Verarbeitung . Ich kann mir keine Möglichkeit vorstellen, den Generator zurückzuspulen, ohne die Werte zu speichern oder erneut zu berechnen.
quelle
quelle
Die wahrscheinlich einfachste Lösung besteht darin, das teure Teil in ein Objekt zu wickeln und an den Generator weiterzugeben:
Auf diese Weise können Sie die teuren Berechnungen zwischenspeichern.
Wenn Sie alle Ergebnisse gleichzeitig im RAM speichern können, verwenden Sie diese Option, um
list()
die Ergebnisse des Generators in einer einfachen Liste zu materialisieren und damit zu arbeiten.quelle
Ich möchte eine andere Lösung für ein altes Problem anbieten
Der Vorteil davon im Vergleich zu so etwas
list(iterator)
ist, dass diesO(1)
Raumkomplexität ist undlist(iterator)
istO(n)
. Der Nachteil ist, dass Sie diese Methode nicht verwenden können, wenn Sie nur Zugriff auf den Iterator haben, nicht jedoch auf die Funktion, die den Iterator erzeugt hat. Zum Beispiel mag es vernünftig erscheinen, Folgendes zu tun, aber es wird nicht funktionieren.quelle
Wenn die Antwort von GrzegorzOledzki nicht ausreicht, könnten Sie sie wahrscheinlich verwenden
send()
, um Ihr Ziel zu erreichen. Weitere Informationen zu erweiterten Generatoren und Ertragsausdrücken finden Sie in PEP-0342 .UPDATE: Siehe auch
itertools.tee()
. Es beinhaltet einen Teil des oben erwähnten Kompromisses zwischen Speicher und Verarbeitung, aber es kann etwas Speicherplatz sparen , wenn nur die Generatorergebnisse gespeichert werdenlist
. Dies hängt davon ab, wie Sie den Generator verwenden.quelle
Wenn Ihr Generator in dem Sinne rein ist, dass seine Ausgabe nur von übergebenen Argumenten und der Schrittnummer abhängt und Sie möchten, dass der resultierende Generator neu gestartet werden kann, ist hier ein Sortierausschnitt, der nützlich sein kann:
Ausgänge:
quelle
Aus der offiziellen Dokumentation des T-Stücks :
Verwenden Sie es
list(iterable)
stattdessen am besten in Ihrem Fall.quelle
list()
legt das Ganze iterable in Erinnerungtee()
Wenn ein Iterator alle Werte verbraucht,tee
funktioniert das auch.Verwenden einer Wrapper-Funktion zur Handhabung
StopIteration
Sie können eine einfache Wrapper-Funktion in Ihre Generator-Generierungsfunktion schreiben, die nachverfolgt, wann der Generator erschöpft ist. Dies geschieht mit der
StopIteration
Ausnahme, die ein Generator auslöst, wenn das Ende der Iteration erreicht ist.Wie Sie oben sehen können,
StopIteration
initialisiert unsere Wrapper-Funktion, wenn sie eine Ausnahme abfängt, das Generatorobjekt einfach neu (unter Verwendung einer anderen Instanz des Funktionsaufrufs).Angenommen, Sie definieren Ihre generatorversorgende Funktion wie folgt: Verwenden Sie die Syntax des Python-Funktionsdekorators, um sie implizit zu verpacken:
quelle
Sie können eine Funktion definieren, die Ihren Generator zurückgibt
Jetzt können Sie so oft tun, wie Sie möchten:
quelle
Ich bin mir nicht sicher, was Sie mit teurer Vorbereitung gemeint haben, aber ich denke, Sie haben es tatsächlich
Wenn dies der Fall ist, warum nicht wiederverwenden
data
?quelle
Es gibt keine Option zum Zurücksetzen von Iteratoren. Der Iterator wird normalerweise angezeigt, wenn er durchlaufen wird
next()
Funktion . Die einzige Möglichkeit besteht darin, vor dem Iterieren des Iteratorobjekts eine Sicherungskopie zu erstellen. Überprüfen Sie unten.Erstellen eines Iteratorobjekts mit den Elementen 0 bis 9
Durchlaufen der Funktion next (), die herausspringt
Konvertieren des Iteratorobjekts in eine Liste
Punkt 0 ist also bereits herausgesprungen. Außerdem werden alle Elemente angezeigt, wenn wir den Iterator in eine Liste konvertieren.
Sie müssen den Iterator also in Listen zur Sicherung konvertieren, bevor Sie mit der Iteration beginnen. Liste könnte mit in Iterator konvertiert werden
iter(<list-object>)
quelle
Sie können jetzt verwenden
more_itertools.seekable
(ein Drittanbieter-Tool) verwenden, mit dem Iteratoren zurückgesetzt werden können.Installieren über
> pip install more_itertools
Hinweis: Der Speicherverbrauch steigt, während der Iterator weiterentwickelt wird. Seien Sie daher vorsichtig bei großen Iterables.
quelle
Mit itertools.cycle () können Sie einen Iterator mit dieser Methode erstellen und dann eine for-Schleife über den Iterator ausführen, die seine Werte durchläuft .
Beispielsweise:
generiert 20 Zahlen, 0 bis 4 wiederholt.
Ein Hinweis aus den Dokumenten:
quelle
Ok, Sie sagen, Sie möchten einen Generator mehrmals aufrufen, aber die Initialisierung ist teuer ... Was ist mit so etwas?
Alternativ können Sie auch eine eigene Klasse erstellen, die dem Iteratorprotokoll folgt und eine Art 'Reset'-Funktion definiert.
https://docs.python.org/2/library/stdtypes.html#iterator-types http://anandology.com/python-practice-book/iterators.html
quelle
__call__
Meine Antwort löst ein etwas anderes Problem: Wenn die Initialisierung des Generators teuer ist und die Generierung jedes generierten Objekts teuer ist. Wir müssen den Generator jedoch mehrmals in mehreren Funktionen verwenden. Um den Generator und jedes generierte Objekt genau einmal aufzurufen, können wir Threads verwenden und jede der konsumierenden Methoden in einem anderen Thread ausführen. Aufgrund von GIL erreichen wir möglicherweise keine echte Parallelität, aber wir werden unser Ziel erreichen.
Dieser Ansatz hat im folgenden Fall gute Arbeit geleistet: Das Deep-Learning-Modell verarbeitet viele Bilder. Das Ergebnis sind viele Masken für viele Objekte auf dem Bild. Jede Maske verbraucht Speicher. Wir haben ungefähr 10 Methoden, die unterschiedliche Statistiken und Metriken erstellen, aber alle Bilder gleichzeitig aufnehmen. Alle Bilder können nicht in den Speicher passen. Die Methoden können leicht umgeschrieben werden, um den Iterator zu akzeptieren.
Ussage:
quelle
itertools.islice
oder für Asyncaiostream.stream.take
, und dieser Beitrag ermöglicht es Ihnen, dies auf asynchrone / wartendeDies kann durch ein Codeobjekt erfolgen. Hier ist das Beispiel.
1 2 3 4
1 2 3 4
quelle
exec
eine etwas nicht empfohlene Initialisierung für einen solchen einfachen Fall.