Могу ли я reset итератор/генератор в Python? Я использую DictReader и хотел бы reset он (из модуля csv) в начало файла.
Могут ли итераторы быть reset в Python?
Ответ 1
Я вижу много ответов, предлагающих itertools.tee, но игнорируя одно важное предупреждение в документах для него:
Этот itertool может потребовать значительных вспомогательное хранилище (в зависимости от того, как много временных данных сохранены). В общем случае, если один итератор использует большинство или все данные до другой итератор запускается, он быстрее для использования
list()
вместоtee()
.
В принципе, tee
предназначен для тех ситуаций, когда два (или более) клона одного итератора, в то время как "выходить из синхронизации" друг с другом, не делают этого много - скорее, говорят в такой же "окрестности" (несколько предметов сзади или впереди друг друга). Не подходит для проблемы OP с "повторением с самого начала".
L = list(DictReader(...))
, с другой стороны, отлично подходит, если список диктонов может удобно помещаться в памяти. Новый "итератор с самого начала" (очень легкий и низкий накладные расходы) может быть выполнен в любое время с помощью iter(L)
и использоваться частично или полностью без влияния на новые или существующие; другие схемы доступа также легко доступны.
Как справедливо отметили несколько ответов, в конкретном случае csv
вы также можете .seek(0)
базовый файловый объект (довольно частный случай). Я не уверен, что документирован и гарантирован, хотя в настоящее время он работает; это, вероятно, стоит рассмотреть только для действительно огромных файлов csv, в которых list
я рекомендую, поскольку общий подход будет иметь слишком большой объем памяти.
Ответ 2
Если у вас есть файл 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. Это не сработает, если вы первый читатель на полпути через чтение файла, так как у вашего нового читателя будут неожиданные ключи и значения из того места, где вы находитесь в файле.
Ответ 3
Нет. Протокол итератора Python очень прост и предоставляет только один метод (.next()
или __next__()
), а не метод для reset итератора вообще.
Общий шаблон - вместо этого создать новый итератор, используя ту же процедуру снова.
Если вы хотите "сэкономить" итератор, чтобы вы могли вернуться к его началу, вы также можете разветкить итератор, используя itertools.tee
Ответ 4
Существует ошибка в использовании .seek(0)
, о которой говорили Алекс Мартелли и Уилдак выше, а именно, что следующий вызов .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)
Ответ 5
Да, если вы используете numpy.nditer
для создания своего итератора.
>>> lst = [1,2,3,4,5]
>>> itr = numpy.nditer([lst])
>>> itr.next()
1
>>> itr.next()
2
>>> itr.finished
False
>>> itr.reset()
>>> itr.next()
1
Ответ 6
Это, пожалуй, ортогонально исходному вопросу, но можно обернуть итератор в функцию, которая возвращает итератор.
def get_iter():
return iterator
В reset итератор снова вызывает функцию. Это, конечно, тривиально, если функция, когда указанная функция не принимает аргументов.
В случае, когда функция требует некоторых аргументов, используйте functools.partial, чтобы создать закрытие, которое может быть передано вместо исходного итератора.
def get_iter(arg1, arg2):
return iterator
from functools import partial
iter_clos = partial(get_iter, a1, a2)
Это, похоже, не позволяет кэшировать, что tee (n копий) или список (1 экземпляр) нужно будет делать
Ответ 7
Пока нет итератора reset, модуль 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]
Ответ 8
list(generator())
возвращает все оставшиеся значения для генератора и эффективно сбрасывает его, если он не зациклен.
Ответ 9
Для небольших файлов вы можете использовать more_itertools.seekable
- сторонний инструмент, который предлагает сбросить итерации.
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))
Выход
{'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'}
Здесь a DictReader
заключен в объект seekable
(1) и расширенный (2). Метод seek()
используется для reset/перемотки итератора в 0-ю позицию (3).
Примечание: потребление памяти растет с итерацией, поэтому будьте осторожны с применением этого инструмента для больших файлов, так как указан в документах.
Ответ 10
Проблема
У меня была такая же проблема. После анализа моего кода я понял, что попытка reset итератора внутри циклов немного увеличивает временную сложность, а также делает код немного уродливым.
Решение
Откройте файл и сохраните строки в переменной в памяти.
# 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)
Теперь вы можете циклически перемещаться по строкам в любом месте своей области без обращения к итератору.
Ответ 11
Только если базовый тип предоставляет механизм для этого (например, fp.seek(0)
).
Ответ 12
Для DictReader:
f = open(filename, "rb")
d = csv.DictReader(f, delimiter=",")
f.seek(0)
d.__init__(f, delimiter=",")
Для 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()
Ответ 13
Один из возможных вариантов - использовать itertools.cycle()
, что позволит вам выполнять итерации бесконечно без каких-либо уловок, подобных .seek(0)
.
iterDic = itertools.cycle(csv.DictReader(open('file.csv')))
Ответ 14
Я прихожу к этой же проблеме - хотя мне нравится решение tee()
, я не знаю, насколько большими будут мои файлы, и предупреждения памяти о том, что один из них потребляется первым, а потом другой, отталкивают меня от принятия этого метода.
Вместо этого я создаю пару итераторов, используя операторы iter()
, и использую первый для моего начального прогона, прежде чем переключиться на второй для финального прогона.
Так, в случае читателя, если он определен с использованием:
d = csv.DictReader(f, delimiter=",")
Я могу создать пару итераторов из этой "спецификации" - используя:
d1, d2 = iter(d), iter(d)
Затем я могу запустить свой код 1-го прохода для d1
, будучи уверенным в том, что второй итератор d2
был определен из той же корневой спецификации.
Я не проверял это исчерпывающе, но, похоже, он работает с фиктивными данными.