list()Кстати , я обнаружил, что функция будет перебирать свой аргумент (итерация). Таким образом, list()дважды вызывая одну и ту же итерацию (например, результат zip()), вы получите пустой список при втором вызове!
theaws.blog
Ответы:
84
Я вижу много ответов, предлагающих itertools.tee , но игнорирую одно важное предупреждение в документации для него:
Этот инструмент itertool может потребовать значительного объема вспомогательной памяти (в зависимости от того, сколько временных данных необходимо сохранить). В общем случае, если один итератор использует большую часть или все данные до запуска другого итератора, его проще использовать list()вместо tee().
По сути, teeон разработан для тех ситуаций, когда два (или более) клона одного итератора, «рассинхронизировавшись» друг с другом, не делают этого слишком сильно - скорее, они говорят, что находятся в одной «окрестности» ( несколько элементов позади или впереди друг друга). Не подходит для задачи OP «повторить с самого начала».
L = list(DictReader(...))с другой стороны, идеально подходит, если список dicts может удобно поместиться в памяти. Новый «итератор с самого начала» (очень легкий и не требующий больших затрат) может быть создан в любое время iter(L)и использован частично или полностью, не затрагивая новые или существующие; другие схемы доступа также легко доступны.
Как правильно отмечено в нескольких ответах, в конкретном случае csvвы также можете .seek(0)использовать базовый файловый объект (довольно особый случай). Я не уверен, что это задокументировано и гарантировано, хотя в настоящее время это работает; его, вероятно, стоит рассматривать только для действительно огромных файлов csv, для которых listя рекомендую в качестве общего подхода слишком большой объем памяти.
Использование list()для кеширования многопроходного режима через csvreader в файле размером 5 МБ приводит к тому, что мое время выполнения меняется с ~ 12 секунд до ~ 0,5 секунды.
Джон Ми
33
Если у вас есть файл csv с именем 'blah.csv'. Это выглядит так:
a,b,c,d1,2,3,42,3,4,53,4,5,6
вы знаете, что можете открыть файл для чтения и создать DictReader с
Затем вы сможете получить следующую строку 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. Это не сработает, если вы первый читатель на полпути к чтению файла, так как у вашего нового читателя будут неожиданные ключи и значения из любого места в файле.
Это было то, что мне сказала моя теория, и приятно видеть, что то, что я думал, должно произойти, действительно происходит.
Уэйн Вернер
@Wilduck: поведение, которое вы описываете с другим экземпляром DictReader, не произойдет, если вы создадите новый дескриптор файла и передадите его второму DictReader, верно?
Если у вас есть два обработчика файлов, они будут вести себя независимо, да.
Wilduck
24
Нет. Протокол итератора в Python очень прост и предоставляет только один единственный метод ( .next()или __next__()) и не имеет метода сброса итератора в целом.
Обычный шаблон - вместо этого создать новый итератор, снова используя ту же процедуру.
Если вы хотите «сэкономить» итератор, чтобы вернуться к его началу, вы также можете выполнить форк итератора, используя itertools.tee
Хотя ваш анализ метода .next (), вероятно, верен, есть довольно простой способ получить то, что запрашивает оператор.
Wilduck
2
@Wilduck: Я вижу ваш ответ. Я только что ответил на вопрос итератора и понятия не имею о csvмодуле. Надеюсь, оба ответа будут полезны для исходного плаката.
u0b34a0f6ae
Строго говоря, протокол итератора также требует __iter__. То есть итераторы также должны быть итерируемыми.
Стив Джессоп
11
Да , если вы используете numpy.nditerдля создания своего итератора.
Есть ошибка в использовании, которую .seek(0)пропагандировали Алекс Мартелли и Уилдак выше, а именно то, что следующий вызов to .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)
Возможно, это ортогонально исходному вопросу, но можно обернуть итератор в функцию, которая возвращает итератор.
def get_iter():return iterator
Чтобы сбросить итератор, просто вызовите функцию еще раз. Это, конечно, тривиально, если функция, когда указанная функция не принимает аргументов.
В случае, если функции требуются некоторые аргументы, используйте functools.partial для создания замыкания, которое можно передать вместо исходного итератора.
Для небольших файлов вы можете рассмотреть возможность использования more_itertools.seekable- стороннего инструмента, который предлагает сброс итераций.
Хотя сброса итератора отсутствует, в модуле 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]
У меня раньше была такая же проблема. Проанализировав свой код, я понял, что попытка сбросить итератор внутри циклов немного увеличивает временную сложность, а также делает код немного некрасивым.
Решение
Откройте файл и сохраните строки в переменной в памяти.
# 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 readerfor row in myfilereader:# add the row to the list of rows
rows.append(row)
Теперь вы можете перебирать строки в любом месте вашей области без использования итератора.
Я прихожу к той же проблеме - хотя мне нравится tee()решение, я не знаю, насколько большими будут мои файлы, и предупреждения памяти о том, что сначала они потребляют один, а затем другой, отталкивают меня от принятия этого метода.
Вместо этого я создаю пару итераторов с помощью iter()операторов и использую первый для моего начального прогона, прежде чем переключиться на второй для окончательного прогона.
Итак, в случае dict-reader, если читатель определен с помощью:
d = csv.DictReader(f, delimiter=",")
Я могу создать пару итераторов из этой «спецификации» - используя:
d1, d2 = iter(d), iter(d)
Затем я могу запустить свой код 1-го прохода d1, зная, что второй итератор d2определен из той же корневой спецификации.
Я не проверял это полностью, но, похоже, он работает с фиктивными данными.
list()
Кстати , я обнаружил, что функция будет перебирать свой аргумент (итерация). Таким образом,list()
дважды вызывая одну и ту же итерацию (например, результатzip()
), вы получите пустой список при втором вызове!Ответы:
Я вижу много ответов, предлагающих itertools.tee , но игнорирую одно важное предупреждение в документации для него:
По сути,
tee
он разработан для тех ситуаций, когда два (или более) клона одного итератора, «рассинхронизировавшись» друг с другом, не делают этого слишком сильно - скорее, они говорят, что находятся в одной «окрестности» ( несколько элементов позади или впереди друг друга). Не подходит для задачи OP «повторить с самого начала».L = list(DictReader(...))
с другой стороны, идеально подходит, если список dicts может удобно поместиться в памяти. Новый «итератор с самого начала» (очень легкий и не требующий больших затрат) может быть создан в любое времяiter(L)
и использован частично или полностью, не затрагивая новые или существующие; другие схемы доступа также легко доступны.Как правильно отмечено в нескольких ответах, в конкретном случае
csv
вы также можете.seek(0)
использовать базовый файловый объект (довольно особый случай). Я не уверен, что это задокументировано и гарантировано, хотя в настоящее время это работает; его, вероятно, стоит рассматривать только для действительно огромных файлов csv, для которыхlist
я рекомендую в качестве общего подхода слишком большой объем памяти.источник
list()
для кеширования многопроходного режима через csvreader в файле размером 5 МБ приводит к тому, что мое время выполнения меняется с ~ 12 секунд до ~ 0,5 секунды.Если у вас есть файл csv с именем 'blah.csv'. Это выглядит так:
вы знаете, что можете открыть файл для чтения и создать DictReader с
Затем вы сможете получить следующую строку
reader.next()
, которая должна выводитьповторное использование произведет
Однако на этом этапе, если вы используете
blah.seek(0)
, в следующий раз, когда вы позвоните,reader.next()
вы получитеочередной раз.
Кажется, это именно та функциональность, которую вы ищете. Я уверен, что с этим подходом связаны некоторые уловки, о которых я не знаю. @Brian предложил просто создать еще один DictReader. Это не сработает, если вы первый читатель на полпути к чтению файла, так как у вашего нового читателя будут неожиданные ключи и значения из любого места в файле.
источник
Нет. Протокол итератора в Python очень прост и предоставляет только один единственный метод (
.next()
или__next__()
) и не имеет метода сброса итератора в целом.Обычный шаблон - вместо этого создать новый итератор, снова используя ту же процедуру.
Если вы хотите «сэкономить» итератор, чтобы вернуться к его началу, вы также можете выполнить форк итератора, используя
itertools.tee
источник
csv
модуле. Надеюсь, оба ответа будут полезны для исходного плаката.__iter__
. То есть итераторы также должны быть итерируемыми.Да , если вы используете
numpy.nditer
для создания своего итератора.источник
nditer
циклично по массиву вродеitertools.cycle
?try:
next()
StopIteration
reset()
next()
Есть ошибка в использовании, которую
.seek(0)
пропагандировали Алекс Мартелли и Уилдак выше, а именно то, что следующий вызов to.next()
даст вам словарь строки заголовка в форме{key1:key1, key2:key2, ...}
. Чтобы обойти эту проблему, нужно выполнитьfile.seek(0)
вызов, чтобыreader.next()
избавиться от строки заголовка.Итак, ваш код будет выглядеть примерно так:
источник
Возможно, это ортогонально исходному вопросу, но можно обернуть итератор в функцию, которая возвращает итератор.
Чтобы сбросить итератор, просто вызовите функцию еще раз. Это, конечно, тривиально, если функция, когда указанная функция не принимает аргументов.
В случае, если функции требуются некоторые аргументы, используйте functools.partial для создания замыкания, которое можно передать вместо исходного итератора.
Кажется, это позволяет избежать кеширования, которое потребуется выполнить tee (n копий) или list (1 копия).
источник
Для небольших файлов вы можете рассмотреть возможность использования
more_itertools.seekable
- стороннего инструмента, который предлагает сброс итераций.демонстрация
Вывод
Здесь a
DictReader
заключен вseekable
объект (1) и расширен (2).seek()
Метод используется для сброса / назад итератора в 0 - ом положении (3).Примечание: потребление памяти растет с итерацией, поэтому будьте осторожны, применяя этот инструмент к большим файлам, как указано в документации .
источник
Хотя сброса итератора отсутствует, в модуле itertools из Python 2.6 (и новее) есть несколько утилит, которые могут здесь помочь. Одним из них является «тройник», который может создавать несколько копий итератора и кэшировать результаты предыдущего, так что эти результаты используются в копиях. Я разделю ваши цели:
источник
Для DictReader:
Для DictWriter:
источник
list(generator())
возвращает все оставшиеся значения для генератора и эффективно сбрасывает его, если он не зациклен.источник
проблема
У меня раньше была такая же проблема. Проанализировав свой код, я понял, что попытка сбросить итератор внутри циклов немного увеличивает временную сложность, а также делает код немного некрасивым.
Решение
Откройте файл и сохраните строки в переменной в памяти.
Теперь вы можете перебирать строки в любом месте вашей области без использования итератора.
источник
Один из возможных вариантов - использовать
itertools.cycle()
, который позволит вам выполнять итерацию бесконечно без каких-либо уловок вроде.seek(0)
.источник
Я прихожу к той же проблеме - хотя мне нравится
tee()
решение, я не знаю, насколько большими будут мои файлы, и предупреждения памяти о том, что сначала они потребляют один, а затем другой, отталкивают меня от принятия этого метода.Вместо этого я создаю пару итераторов с помощью
iter()
операторов и использую первый для моего начального прогона, прежде чем переключиться на второй для окончательного прогона.Итак, в случае dict-reader, если читатель определен с помощью:
Я могу создать пару итераторов из этой «спецификации» - используя:
Затем я могу запустить свой код 1-го прохода
d1
, зная, что второй итераторd2
определен из той же корневой спецификации.Я не проверял это полностью, но, похоже, он работает с фиктивными данными.
источник
Вернуть вновь созданный итератор на последней итерации во время вызова iter ()
Вывод:
источник
Только если базовый тип предоставляет механизм для этого (например,
fp.seek(0)
).источник