Можно ли сбросить итераторы в Python?
могу ли я сбросить итератор / генератор в Python? Я использую DictReader и хотел бы сбросить его (из модуля csv) в начало файла.
12 ответов
Я вижу много ответов, предлагая itertools.ти, но это игнорирует одно важное предупреждение в документации к ней:
этот itertool может потребовать значительного вспомогательной памяти (в зависимости от много временных данных должно быть хранящийся.) В общем случае, если один итератор использует большинство или все данные перед запускается другой итератор, он быстрее использовать
list()
вместоtee()
.
по сути, tee
предназначен для тех, ситуация, когда два (или более) клона одного итератора, "выходя из синхронизации" друг с другом, не делают этого много -- скорее, они говорят в одной и той же "близости" (несколько предметов позади или впереди друг друга). Не подходит для проблемы ОП "повторить с самого начала".
L = list(DictReader(...))
С другой стороны, идеально подходит, пока список диктов может удобно поместиться в памяти. Новый "итератор с самого начала" (очень легкий и с низкими накладными расходами) может быть сделан на в любое время с iter(L)
, и использованный частично или в целом без влияния новых или существующих одних; другие картины доступа также легко доступны.
как справедливо заметили несколько ответов, в конкретном случае csv
вы можете .seek(0)
базовый файловый объект (довольно частный случай). Я не уверен, что это документировано и гарантировано, хотя в настоящее время это работает; вероятно, стоит рассмотреть только для действительно огромных csv-файлов, в которых list
я рассказывать об Как общий подход будет иметь слишком большой объем памяти.
если у вас есть файл csv с именем 'бла.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()
вы get
{'a':1,'b':2,'c':3,'d':4}
снова.
это, кажется, функциональность, которую вы ищете. Я уверен, что есть некоторые трюки, связанные с этим подходом, о которых я не знаю. @Brian предложил просто создать еще один диктофон. Это не будет работать, если вы первый читатель на полпути через чтение файла, как ваш новый читатель будет иметь неожиданные ключи и значения из любого места в файле.
нет. Протокол итератора Python очень прост и предоставляет только один единственный метод (.next()
или __next__()
), и нет метода сброса итератора в целом.
общий шаблон состоит в том, чтобы вместо этого создать новый итератор, используя ту же процедуру снова.
Если вы хотите "сохранить" итератор, чтобы вы могли вернуться к его началу, вы также можете развить итератор с помощью itertools.tee
в использовании есть ошибка .искать(0), как отстаивали Алекс Мартелли и Вилдак выше, а именно, что следующий вызов .next () даст вам словарь строки заголовка в виде {key1:key1, key2:key2, ...}. Работа вокруг, чтобы следовать файл.поиск(0) с вызовом reader.следующий() чтобы избавиться от строки заголовка.
Так что ваш код будет выглядеть примерно так:
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)
Да, если вы используете 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
пока нет сброса итератора, модуль "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]
это, возможно, ортогонально исходному вопросу, но можно обернуть итератор в функцию, которая возвращает итератор.
def get_iter():
return iterator
сбросить итератор просто снова вызовите функцию. Это, конечно, тривиально, если функция, когда указанная функция не принимает аргументов.
в случае, если функция требует аргументов, использовать functools.partial для создания закрытия, которое может быть передано вместо исходного итератор.
def get_iter(arg1, arg2):
return iterator
from functools import partial
iter_clos = partial(get_iter, a1, a2)
это, похоже, позволяет избежать кэширования, которое tee (N копий) или list (1 копия) должны были бы сделать
для небольших файлов, вы можете использовать more_itertools.seekable
- сторонний инструмент, который предлагает сброс iterables.
демо
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'}
на DictReader
обернут вДля 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()
list(generator())
возвращает все оставшиеся значения для генератора и эффективно сбрасывает его, если он не зациклен.