Итераторы могут быть сброшены в Python?

Я могу сбросить итератор / генератор в Python? Я использую DictReader и хотел бы сбросить его (от csv модуля) к началу файла.

112
задан thefourtheye 22 January 2014 в 13:08
поделиться

6 ответов

Я вижу много ответов, предлагающих itertools.tee , но при этом игнорируется одно важное предупреждение в документации для него:

Этот инструмент itertool может потребовать значительных вспомогательное хранилище (в зависимости от того, как нужно много временных данных хранится). В общем, если один итератор использует большую часть или все данные до запускается другой итератор, он быстрее использовать list () вместо tee () .

По сути, tee разработан для тех ситуаций, когда два (или более) клона одного итератора, «рассинхронизировавшись» друг с другом, не делают этого в значительной степени - точнее, в одной «окрестности» (несколько предметов позади или впереди друг друга). Не подходит для задачи OP «повторить с самого начала».

L = list (DictReader (...)) , с другой стороны, идеально подходит, если список dicts может удобно поместиться в памяти. Новый «итератор с самого начала» (очень легкий и не требующий больших затрат) может быть создан в любое время с помощью iter (L) и использован частично или полностью, не затрагивая новые или существующие; другие схемы доступа также легко доступны.

Как правильно отмечено в нескольких ответах, в конкретном случае csv вы также можете .seek (0) базовый файловый объект (довольно частный случай). Я не уверен, что это задокументировано и гарантировано, хотя в настоящее время это работает; его, вероятно, стоит рассматривать только для действительно огромных файлов csv, в которых я рекомендую список , поскольку общий подход потребует слишком большого объема памяти.

74
ответ дан 24 November 2019 в 02:52
поделиться

Нет. Протокол итератора в Python очень прост и предоставляет только один единственный метод (.next() или __next__()), и нет метода для сброса итератора в целом.

Общим шаблоном является создание нового итератора с помощью той же процедуры.

Если вы хотите "отложить" итератор, чтобы вернуться к его началу, вы также можете форкнуть итератор с помощью itertools.tee

22
ответ дан 24 November 2019 в 02:52
поделиться

Если у вас есть файл 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. Это не сработает, если ваш первый ридер находится на полпути чтения файла, поскольку ваш новый ридер будет иметь неожиданные ключи и значения из того места, где вы находитесь в файле.

30
ответ дан 24 November 2019 в 02:52
поделиться

Существует ошибка в использовании .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)
11
ответ дан 24 November 2019 в 02:52
поделиться

Хотя сброса итератора отсутствует, в модуле 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]
2
ответ дан 24 November 2019 в 02:52
поделиться

Только если базовый тип предоставляет механизм для этого (например, fp.seek (0) ).

0
ответ дан 24 November 2019 в 02:52
поделиться
Другие вопросы по тегам:

Похожие вопросы: