Can iterators be reset in Python?
--
Music by Eric Matyas
https://www.soundimage.org
Track title: Lost Jungle Looping
--
Chapters
00:00 Question
00:23 Accepted answer (Score 99)
01:58 Answer 2 (Score 39)
03:07 Answer 3 (Score 31)
03:42 Answer 4 (Score 11)
03:59 Thank you
--
Full question
https://stackoverflow.com/questions/3266...
Accepted answer links:
[itertools.tee]: http://docs.python.org/library/itertools...
--
Content licensed under CC BY-SA
https://meta.stackexchange.com/help/lice...
--
Tags
#python #iterator #generator
#avk47
ACCEPTED ANSWER
Score 103
I see many answers suggesting itertools.tee, but that's ignoring one crucial warning in the docs for it:
This itertool may require significant auxiliary storage (depending on how much temporary data needs to be stored). In general, if one iterator uses most or all of the data before another iterator starts, it is faster to use
list()instead oftee().
Basically, tee is designed for those situation where two (or more) clones of one iterator, while "getting out of sync" with each other, don't do so by much -- rather, they say in the same "vicinity" (a few items behind or ahead of each other). Not suitable for the OP's problem of "redo from the start".
L = list(DictReader(...)) on the other hand is perfectly suitable, as long as the list of dicts can fit comfortably in memory. A new "iterator from the start" (very lightweight and low-overhead) can be made at any time with iter(L), and used in part or in whole without affecting new or existing ones; other access patterns are also easily available.
As several answers rightly remarked, in the specific case of csv you can also .seek(0) the underlying file object (a rather special case). I'm not sure that's documented and guaranteed, though it does currently work; it would probably be worth considering only for truly huge csv files, in which the list I recommmend as the general approach would have too large a memory footprint.
ANSWER 2
Score 41
If you have a csv file named 'blah.csv' That looks like
a,b,c,d
1,2,3,4
2,3,4,5
3,4,5,6
you know that you can open the file for reading, and create a DictReader with
blah = open('blah.csv', 'r')
reader= csv.DictReader(blah)
Then, you will be able to get the next line with reader.next(), which should output
{'a':1,'b':2,'c':3,'d':4}
using it again will produce
{'a':2,'b':3,'c':4,'d':5}
However, at this point if you use blah.seek(0), the next time you call reader.next() you will get
{'a':1,'b':2,'c':3,'d':4}
again.
This seems to be the functionality you're looking for. I'm sure there are some tricks associated with this approach that I'm not aware of however. @Brian suggested simply creating another DictReader. This won't work if you're first reader is half way through reading the file, as your new reader will have unexpected keys and values from wherever you are in the file.
ANSWER 3
Score 33
No. Python's iterator protocol is very simple, and only provides one single method (.next() or __next__()), and no method to reset an iterator in general.
The common pattern is to instead create a new iterator using the same procedure again.
If you want to "save off" an iterator so that you can go back to its beginning, you may also fork the iterator by using itertools.tee
ANSWER 4
Score 13
There's a bug in using .seek(0) as advocated by Alex Martelli and Wilduck above, namely that the next call to .next() will give you a dictionary of your header row in the form of {key1:key1, key2:key2, ...}. The work around is to follow file.seek(0) with a call to reader.next() to get rid of the header row.
So your code would look something like this:
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)