Ich sehe viele Antworten, die auf itertools.tee hindeuten , aber das ignoriert eine entscheidende Warnung in den Dokumenten dafür:
Dieses Tool erfordert möglicherweise einen erheblichen zusätzlichen Speicher (abhängig davon, wie viele temporäre Daten gespeichert werden müssen). Im Allgemeinen, wenn man verwendet Iterator die meisten oder alle Daten , bevor eine andere Iterator beginnt, ist es schneller zu verwenden list()statt tee().
Grundsätzlich teeist es für Situationen gedacht, in denen zwei (oder mehr) Klone eines Iterators, während sie "nicht mehr synchron" sind, dies nicht viel tun - vielmehr sagen sie in derselben "Umgebung" (a wenige Gegenstände hinter oder voreinander). Nicht geeignet für das OP-Problem "Von Anfang an wiederholen".
L = list(DictReader(...))Auf der anderen Seite ist es perfekt geeignet, solange die Liste der Diktate bequem in Erinnerung bleibt. Ein neuer "Iterator von Anfang an" (sehr leicht und mit geringem Overhead) kann jederzeit mit erstellt iter(L)oder ganz oder teilweise verwendet werden, ohne dass neue oder vorhandene davon betroffen sind. andere Zugriffsmuster sind ebenfalls leicht verfügbar.
Wie mehrere Antworten zu Recht bemerkten, können Sie im konkreten Fall csvauch .seek(0)das zugrunde liegende Dateiobjekt (ein eher spezieller Fall). Ich bin nicht sicher, ob dies dokumentiert und garantiert ist, obwohl es derzeit funktioniert. Es lohnt sich wahrscheinlich, nur für wirklich große CSV-Dateien in Betracht zu ziehen, bei denen listich empfehle, da der allgemeine Ansatz einen zu großen Speicherbedarf hätte.
Dann können Sie die nächste Zeile mit erhalten reader.next(), die ausgegeben werden soll
{'a':1,'b':2,'c':3,'d':4}
Wenn Sie es erneut verwenden, wird es erzeugt
{'a':2,'b':3,'c':4,'d':5}
Wenn Sie jedoch an dieser Stelle verwenden blah.seek(0), das nächste Mal , wenn Sie rufen reader.next()Sie erhalten
{'a':1,'b':2,'c':3,'d':4}
nochmal.
Dies scheint die Funktionalität zu sein, nach der Sie suchen. Ich bin mir sicher, dass mit diesem Ansatz einige Tricks verbunden sind, die mir jedoch nicht bekannt sind. @Brian schlug vor, einfach einen weiteren DictReader zu erstellen. Dies funktioniert nicht, wenn Ihr erster Leser die Datei zur Hälfte gelesen hat, da Ihr neuer Leser unerwartete Schlüssel und Werte von jedem Ort in der Datei hat.
Das sagte mir meine Theorie, schön zu sehen, dass das, was ich dachte, passieren sollte.
Wayne Werner
@ Wilduck: Das Verhalten, das Sie mit einer anderen Instanz von DictReader beschreiben, tritt nicht auf, wenn Sie ein neues Dateihandle erstellen und dieses an den zweiten DictReader übergeben, oder?
Wenn Sie zwei Dateihandler haben, verhalten sich diese unabhängig voneinander, ja.
Wilduck
24
Nein. Das Iteratorprotokoll von Python ist sehr einfach und bietet nur eine einzige Methode ( .next()oder __next__()) und keine Methode zum Zurücksetzen eines Iterators im Allgemeinen.
Das übliche Muster besteht darin, stattdessen erneut einen neuen Iterator mit demselben Verfahren zu erstellen.
Wenn Sie einen Iterator "speichern" möchten, damit Sie zu seinem Anfang zurückkehren können, können Sie den Iterator auch mit verwenden itertools.tee
Während Ihre Analyse der .next () -Methode wahrscheinlich korrekt ist, gibt es einen ziemlich einfachen Weg, um herauszufinden, wonach die Operation fragt.
Wilduck
2
@ Wilduck: Ich sehe, dass deine Antwort. Ich habe gerade die Iteratorfrage beantwortet und habe keine Ahnung von dem csvModul. Hoffentlich sind beide Antworten für das Originalplakat nützlich.
u0b34a0f6ae
Streng genommen erfordert das Iteratorprotokoll auch __iter__. Das heißt, Iteratoren müssen auch iterabel sein.
Steve Jessop
11
Ja , wenn Sie numpy.nditerIhren Iterator erstellen.
Es gibt einen Fehler bei der Verwendung, .seek(0)wie von Alex Martelli und Wilduck oben befürwortet, nämlich, dass Sie beim nächsten Aufruf .next()ein Wörterbuch Ihrer Kopfzeile in Form von erhalten {key1:key1, key2:key2, ...}. Die Problemumgehung besteht darin, file.seek(0)mit einem Aufruf zu folgen reader.next(), um die Kopfzeile zu entfernen.
Ihr Code würde also ungefähr so aussehen:
f_in = open('myfile.csv','r')
reader = csv.DictReader(f_in)for record in reader:if some_condition:# reset reader to first row of data on 2nd line of file
f_in.seek(0)
reader.next()continue
do_something(record)
Dies ist vielleicht orthogonal zur ursprünglichen Frage, aber man könnte den Iterator in eine Funktion einschließen, die den Iterator zurückgibt.
def get_iter():return iterator
Um den Iterator zurückzusetzen, rufen Sie einfach die Funktion erneut auf. Dies ist natürlich trivial, wenn die Funktion, wenn diese Funktion keine Argumente akzeptiert.
Für den Fall, dass die Funktion einige Argumente erfordert, verwenden Sie functools.partial, um einen Abschluss zu erstellen, der anstelle des ursprünglichen Iterators übergeben werden kann.
Für kleine Dateien können Sie die Verwendung in Betracht ziehen more_itertools.seekable eines Tools von Drittanbietern in das das Zurücksetzen von Iterables ermöglicht.
Hier DictReaderwird a in ein seekableObjekt (1) eingewickelt und weiterentwickelt (2). Die seek()Methode wird verwendet, um den Iterator auf die 0. Position (3) zurückzusetzen / zurückzuspulen.
Hinweis: Der Speicherverbrauch steigt mit der Iteration. Seien Sie also vorsichtig, wenn Sie dieses Tool auf große Dateien anwenden, wie in den Dokumenten angegeben .
Während es kein Zurücksetzen des Iterators gibt, verfügt das "itertools" -Modul von Python 2.6 (und höher) über einige Dienstprogramme, die dort helfen können. Eines davon ist das "T-Stück", mit dem mehrere Kopien eines Iterators erstellt und die Ergebnisse des vorauslaufenden Iterers zwischengespeichert werden können, sodass diese Ergebnisse für die Kopien verwendet werden. Ich werde Ihre Zwecke sieben:
>>>def printiter(n):...for i in xrange(n):...print"iterating value %d"% i
...yield i
>>>from itertools import tee
>>> a, b = tee(printiter(5),2)>>> list(a)
iterating value 0
iterating value 1
iterating value 2
iterating value 3
iterating value 4[0,1,2,3,4]>>> list(b)[0,1,2,3,4]
Ich hatte das gleiche Problem schon einmal. Nachdem ich meinen Code analysiert hatte, stellte ich fest, dass der Versuch, den Iterator innerhalb von Schleifen zurückzusetzen, die Zeitkomplexität geringfügig erhöht und den Code auch etwas hässlich macht.
Lösung
Öffnen Sie die Datei und speichern Sie die Zeilen in einer Variablen im Speicher.
# initialize list of rows
rows =[]# open the file and temporarily name it as 'my_file'with open('myfile.csv','rb')as my_file:# set up the reader using the opened file
myfilereader = csv.DictReader(my_file)# loop through each row of the readerfor row in myfilereader:# add the row to the list of rows
rows.append(row)
Jetzt können Sie Zeilen an einer beliebigen Stelle in Ihrem Bereich durchlaufen, ohne sich mit einem Iterator befassen zu müssen.
Ich komme zu dem gleichen Thema - während ich das mag tee() Lösung gefällt, weiß ich nicht, wie groß meine Dateien sein werden, und die Speicherwarnungen vor dem erstmaligen Konsumieren einer Datei vor der anderen hindern mich daran, diese Methode anzuwenden.
Stattdessen erstelle ich ein Paar von Iteratoren mithilfe von iter()Anweisungen und verwende den ersten für meinen ersten Durchlauf, bevor ich für den letzten Durchlauf zum zweiten wechsle.
Also, im Fall eines Diktierlesers, wenn der Leser definiert ist mit:
d = csv.DictReader(f, delimiter=",")
Ich kann aus dieser "Spezifikation" ein Paar Iteratoren erstellen - mit:
d1, d2 = iter(d), iter(d)
Ich kann dann meinen 1st-Pass-Code ausführen d1, sicher in dem Wissen, dass der zweite Iteratord2 aus derselben definiert wurde.
Ich habe dies nicht ausführlich getestet, aber es scheint mit Dummy-Daten zu funktionieren.
Antworten:
Ich sehe viele Antworten, die auf itertools.tee hindeuten , aber das ignoriert eine entscheidende Warnung in den Dokumenten dafür:
Grundsätzlich
tee
ist es für Situationen gedacht, in denen zwei (oder mehr) Klone eines Iterators, während sie "nicht mehr synchron" sind, dies nicht viel tun - vielmehr sagen sie in derselben "Umgebung" (a wenige Gegenstände hinter oder voreinander). Nicht geeignet für das OP-Problem "Von Anfang an wiederholen".L = list(DictReader(...))
Auf der anderen Seite ist es perfekt geeignet, solange die Liste der Diktate bequem in Erinnerung bleibt. Ein neuer "Iterator von Anfang an" (sehr leicht und mit geringem Overhead) kann jederzeit mit erstelltiter(L)
oder ganz oder teilweise verwendet werden, ohne dass neue oder vorhandene davon betroffen sind. andere Zugriffsmuster sind ebenfalls leicht verfügbar.Wie mehrere Antworten zu Recht bemerkten, können Sie im konkreten Fall
csv
auch.seek(0)
das zugrunde liegende Dateiobjekt (ein eher spezieller Fall). Ich bin nicht sicher, ob dies dokumentiert und garantiert ist, obwohl es derzeit funktioniert. Es lohnt sich wahrscheinlich, nur für wirklich große CSV-Dateien in Betracht zu ziehen, bei denenlist
ich empfehle, da der allgemeine Ansatz einen zu großen Speicherbedarf hätte.quelle
list()
ich Multipassage über einen CSV-Reader in einer 5-MB-Datei zwischenspeichere, geht meine Laufzeit von ~ 12 Sekunden auf ~ 0,5 Sekunden.Wenn Sie eine CSV-Datei mit dem Namen 'blah.csv' haben, sieht das so aus
Sie wissen, dass Sie die Datei zum Lesen öffnen und mit einen DictReader erstellen können
Dann können Sie die nächste Zeile mit erhalten
reader.next()
, die ausgegeben werden sollWenn Sie es erneut verwenden, wird es erzeugt
Wenn Sie jedoch an dieser Stelle verwenden
blah.seek(0)
, das nächste Mal , wenn Sie rufenreader.next()
Sie erhaltennochmal.
Dies scheint die Funktionalität zu sein, nach der Sie suchen. Ich bin mir sicher, dass mit diesem Ansatz einige Tricks verbunden sind, die mir jedoch nicht bekannt sind. @Brian schlug vor, einfach einen weiteren DictReader zu erstellen. Dies funktioniert nicht, wenn Ihr erster Leser die Datei zur Hälfte gelesen hat, da Ihr neuer Leser unerwartete Schlüssel und Werte von jedem Ort in der Datei hat.
quelle
Nein. Das Iteratorprotokoll von Python ist sehr einfach und bietet nur eine einzige Methode (
.next()
oder__next__()
) und keine Methode zum Zurücksetzen eines Iterators im Allgemeinen.Das übliche Muster besteht darin, stattdessen erneut einen neuen Iterator mit demselben Verfahren zu erstellen.
Wenn Sie einen Iterator "speichern" möchten, damit Sie zu seinem Anfang zurückkehren können, können Sie den Iterator auch mit verwenden
itertools.tee
quelle
csv
Modul. Hoffentlich sind beide Antworten für das Originalplakat nützlich.__iter__
. Das heißt, Iteratoren müssen auch iterabel sein.Ja , wenn Sie
numpy.nditer
Ihren Iterator erstellen.quelle
nditer
wie durch das Array radelnitertools.cycle
?try:
dasnext()
undStopIteration
ausnahmsweise ein machenreset()
.next()
Es gibt einen Fehler bei der Verwendung,
.seek(0)
wie von Alex Martelli und Wilduck oben befürwortet, nämlich, dass Sie beim nächsten Aufruf.next()
ein Wörterbuch Ihrer Kopfzeile in Form von erhalten{key1:key1, key2:key2, ...}
. Die Problemumgehung besteht darin,file.seek(0)
mit einem Aufruf zu folgenreader.next()
, um die Kopfzeile zu entfernen.Ihr Code würde also ungefähr so aussehen:
quelle
Dies ist vielleicht orthogonal zur ursprünglichen Frage, aber man könnte den Iterator in eine Funktion einschließen, die den Iterator zurückgibt.
Um den Iterator zurückzusetzen, rufen Sie einfach die Funktion erneut auf. Dies ist natürlich trivial, wenn die Funktion, wenn diese Funktion keine Argumente akzeptiert.
Für den Fall, dass die Funktion einige Argumente erfordert, verwenden Sie functools.partial, um einen Abschluss zu erstellen, der anstelle des ursprünglichen Iterators übergeben werden kann.
Dies scheint das Caching zu vermeiden, das Tee (n Kopien) oder Liste (1 Kopie) durchführen müsste
quelle
Für kleine Dateien können Sie die Verwendung in Betracht ziehen
more_itertools.seekable
eines Tools von Drittanbietern in das das Zurücksetzen von Iterables ermöglicht.Demo
Ausgabe
Hier
DictReader
wird a in einseekable
Objekt (1) eingewickelt und weiterentwickelt (2). Dieseek()
Methode wird verwendet, um den Iterator auf die 0. Position (3) zurückzusetzen / zurückzuspulen.Hinweis: Der Speicherverbrauch steigt mit der Iteration. Seien Sie also vorsichtig, wenn Sie dieses Tool auf große Dateien anwenden, wie in den Dokumenten angegeben .
quelle
Während es kein Zurücksetzen des Iterators gibt, verfügt das "itertools" -Modul von Python 2.6 (und höher) über einige Dienstprogramme, die dort helfen können. Eines davon ist das "T-Stück", mit dem mehrere Kopien eines Iterators erstellt und die Ergebnisse des vorauslaufenden Iterers zwischengespeichert werden können, sodass diese Ergebnisse für die Kopien verwendet werden. Ich werde Ihre Zwecke sieben:
quelle
Für DictReader:
Für DictWriter:
quelle
list(generator())
Gibt alle verbleibenden Werte für einen Generator zurück und setzt ihn effektiv zurück, wenn er nicht geloopt ist.quelle
Problem
Ich hatte das gleiche Problem schon einmal. Nachdem ich meinen Code analysiert hatte, stellte ich fest, dass der Versuch, den Iterator innerhalb von Schleifen zurückzusetzen, die Zeitkomplexität geringfügig erhöht und den Code auch etwas hässlich macht.
Lösung
Öffnen Sie die Datei und speichern Sie die Zeilen in einer Variablen im Speicher.
Jetzt können Sie Zeilen an einer beliebigen Stelle in Ihrem Bereich durchlaufen, ohne sich mit einem Iterator befassen zu müssen.
quelle
Eine mögliche Option ist die Verwendung
itertools.cycle()
, mit der Sie unbegrenzt iterieren können, ohne einen Trick wie.seek(0)
.quelle
Ich komme zu dem gleichen Thema - während ich das mag
tee()
Lösung gefällt, weiß ich nicht, wie groß meine Dateien sein werden, und die Speicherwarnungen vor dem erstmaligen Konsumieren einer Datei vor der anderen hindern mich daran, diese Methode anzuwenden.Stattdessen erstelle ich ein Paar von Iteratoren mithilfe von
iter()
Anweisungen und verwende den ersten für meinen ersten Durchlauf, bevor ich für den letzten Durchlauf zum zweiten wechsle.Also, im Fall eines Diktierlesers, wenn der Leser definiert ist mit:
Ich kann aus dieser "Spezifikation" ein Paar Iteratoren erstellen - mit:
Ich kann dann meinen 1st-Pass-Code ausführen
d1
, sicher in dem Wissen, dass der zweite Iteratord2
aus derselben definiert wurde.Ich habe dies nicht ausführlich getestet, aber es scheint mit Dummy-Daten zu funktionieren.
quelle
Nur wenn der zugrunde liegende Typ einen Mechanismus dafür bereitstellt (z
fp.seek(0)
. B. ).quelle
Gibt einen neu erstellten Iterator bei der letzten Iteration während des Aufrufs von 'iter ()' zurück
Ausgabe:
quelle