Kann ich einen Iterator / Generator in Python zurücksetzen? Ich verwende DictReader und möchte es auf den Anfang der Datei zurücksetzen.
Kann ich einen Iterator / Generator in Python zurücksetzen? Ich verwende DictReader und möchte es auf den Anfang der Datei zurücksetzen.
Antworten:
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()
statttee()
.
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 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 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 denen list
ich empfehle, da der allgemeine Ansatz einen zu großen Speicherbedarf hätte.
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
a,b,c,d
1,2,3,4
2,3,4,5
3,4,5,6
Sie wissen, dass Sie die Datei zum Lesen öffnen und mit einen DictReader erstellen können
blah = open('blah.csv', 'r')
reader= csv.DictReader(blah)
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.
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
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.
>>> lst = [1,2,3,4,5]
>>> itr = numpy.nditer([lst])
>>> itr.next()
1
>>> itr.next()
2
>>> itr.finished
False
>>> itr.reset()
>>> itr.next()
1
nditer
wie durch das Array radeln itertools.cycle
?
try:
das next()
und StopIteration
ausnahmsweise ein machen reset()
.
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 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.
def get_iter(arg1, arg2):
return iterator
from functools import partial
iter_clos = partial(get_iter, a1, a2)
Dies scheint das Caching zu vermeiden, das Tee (n Kopien) oder Liste (1 Kopie) durchführen müsste
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
import csv
import more_itertools as mit
filename = "data/iris.csv"
with open(filename, "r") as f:
reader = csv.DictReader(f)
iterable = mit.seekable(reader) # 1
print(next(iterable)) # 2
print(next(iterable))
print(next(iterable))
print("\nReset iterable\n--------------")
iterable.seek(0) # 3
print(next(iterable))
print(next(iterable))
print(next(iterable))
Ausgabe
{'Sepal width': '3.5', 'Petal width': '0.2', 'Petal length': '1.4', 'Sepal length': '5.1', 'Species': 'Iris-setosa'}
{'Sepal width': '3', 'Petal width': '0.2', 'Petal length': '1.4', 'Sepal length': '4.9', 'Species': 'Iris-setosa'}
{'Sepal width': '3.2', 'Petal width': '0.2', 'Petal length': '1.3', 'Sepal length': '4.7', 'Species': 'Iris-setosa'}
Reset iterable
--------------
{'Sepal width': '3.5', 'Petal width': '0.2', 'Petal length': '1.4', 'Sepal length': '5.1', 'Species': 'Iris-setosa'}
{'Sepal width': '3', 'Petal width': '0.2', 'Petal length': '1.4', 'Sepal length': '4.9', 'Species': 'Iris-setosa'}
{'Sepal width': '3.2', 'Petal width': '0.2', 'Petal length': '1.3', 'Sepal length': '4.7', 'Species': 'Iris-setosa'}
Hier DictReader
wird a in ein seekable
Objekt (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]
Für DictReader:
f = open(filename, "rb")
d = csv.DictReader(f, delimiter=",")
f.seek(0)
d.__init__(f, delimiter=",")
Für DictWriter:
f = open(filename, "rb+")
d = csv.DictWriter(f, fieldnames=fields, delimiter=",")
f.seek(0)
f.truncate(0)
d.__init__(f, fieldnames=fields, delimiter=",")
d.writeheader()
f.flush()
list(generator())
Gibt alle verbleibenden Werte für einen Generator zurück und setzt ihn effektiv zurück, wenn er nicht geloopt ist.
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.
Ö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 reader
for 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.
Eine mögliche Option ist die Verwendung itertools.cycle()
, mit der Sie unbegrenzt iterieren können, ohne einen Trick wie .seek(0)
.
iterDic = itertools.cycle(csv.DictReader(open('file.csv')))
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.
Nur wenn der zugrunde liegende Typ einen Mechanismus dafür bereitstellt (z fp.seek(0)
. B. ).
Gibt einen neu erstellten Iterator bei der letzten Iteration während des Aufrufs von 'iter ()' zurück
class ResetIter:
def __init__(self, num):
self.num = num
self.i = -1
def __iter__(self):
if self.i == self.num-1: # here, return the new object
return self.__class__(self.num)
return self
def __next__(self):
if self.i == self.num-1:
raise StopIteration
if self.i <= self.num-1:
self.i += 1
return self.i
reset_iter = ResetRange(10)
for i in reset_iter:
print(i, end=' ')
print()
for i in reset_iter:
print(i, end=' ')
print()
for i in reset_iter:
print(i, end=' ')
Ausgabe:
0 1 2 3 4 5 6 7 8 9
0 1 2 3 4 5 6 7 8 9
0 1 2 3 4 5 6 7 8 9