Я могу сбросить итератор / генератор в Python? Я использую DictReader и хотел бы сбросить его (от csv модуля) к началу файла.
Я вижу много ответов, предлагающих itertools.tee , но при этом игнорируется одно важное предупреждение в документации для него:
Этот инструмент itertool может потребовать значительных вспомогательное хранилище (в зависимости от того, как нужно много временных данных хранится). В общем, если один итератор использует большую часть или все данные до запускается другой итератор, он быстрее использовать
list ()
вместоtee ()
.
По сути, tee
разработан для тех ситуаций, когда два (или более) клона одного итератора, «рассинхронизировавшись» друг с другом, не делают этого в значительной степени - точнее, в одной «окрестности» (несколько предметов позади или впереди друг друга). Не подходит для задачи OP «повторить с самого начала».
L = list (DictReader (...))
, с другой стороны, идеально подходит, если список dicts может удобно поместиться в памяти. Новый «итератор с самого начала» (очень легкий и не требующий больших затрат) может быть создан в любое время с помощью iter (L)
и использован частично или полностью, не затрагивая новые или существующие; другие схемы доступа также легко доступны.
Как правильно отмечено в нескольких ответах, в конкретном случае csv
вы также можете .seek (0)
базовый файловый объект (довольно частный случай). Я не уверен, что это задокументировано и гарантировано, хотя в настоящее время это работает; его, вероятно, стоит рассматривать только для действительно огромных файлов csv, в которых я рекомендую список
, поскольку общий подход потребует слишком большого объема памяти.
Нет. Протокол итератора в Python очень прост и предоставляет только один единственный метод (.next()
или __next__()
), и нет метода для сброса итератора в целом.
Общим шаблоном является создание нового итератора с помощью той же процедуры.
Если вы хотите "отложить" итератор, чтобы вернуться к его началу, вы также можете форкнуть итератор с помощью itertools.tee
Если у вас есть файл csv с именем 'blah.csv', который выглядит как
a,b,c,d
1,2,3,4
2,3,4,5
3,4,5,6
вы знаете, что можете открыть файл для чтения и создать DictReader с
blah = open('blah.csv', 'r')
reader= csv.DictReader(blah)
Затем вы сможете получить следующую строку с помощью reader. next()
, который должен вывести
{'a':1,'b':2,'c':3,'d':4}
использование его снова даст результат
{'a':2,'b':3,'c':4,'d':5}
Однако в этот момент, если вы используете blah.seek(0)
, то при следующем вызове reader.next()
вы получите
{'a':1,'b':2,'c':3,'d':4}
снова.
Похоже, это и есть та функциональность, которую вы ищете. Я уверен, что с этим подходом связаны некоторые хитрости, о которых я не знаю. @Brian предложил просто создать еще один DictReader. Это не сработает, если ваш первый ридер находится на полпути чтения файла, поскольку ваш новый ридер будет иметь неожиданные ключи и значения из того места, где вы находитесь в файле.
Существует ошибка в использовании .seek(0)
, за которую выступают Alex Martelli и Wilduck выше, а именно: следующий вызов .next()
даст вам словарь строки заголовка в виде {key1:key1, key2:key2, ...}
. Чтобы избавиться от строки заголовка, нужно после file.seek(0)
вызвать reader.next()
.
Таким образом, ваш код будет выглядеть примерно так:
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)
Хотя сброса итератора отсутствует, в модуле itertools из Python 2.6 (и более поздних версий) есть некоторые утилиты, которые могут помочь в этом. Одним из них является «тройник», который может создавать несколько копий итератора и кэшировать результаты предыдущего, так что эти результаты используются в копиях. Я разделю ваши цели:
>>> 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]
Только если базовый тип предоставляет механизм для этого (например, fp.seek (0)
).