Чи можу я скинути ітератор / генератор в Python? Я використовую DictReader і хотів би відновити його до початку файлу.
Чи можу я скинути ітератор / генератор в Python? Я використовую DictReader і хотів би відновити його до початку файлу.
Відповіді:
Я бачу багато відповідей, що підказують itertools.tee , але це ігнорування одного важливого попередження в документах для цього:
Цей itertool може потребувати значного допоміжного зберігання (залежно від того, скільки тимчасових даних потрібно зберігати). Загалом, якщо один ітератор використовує більшість або всі дані до запуску іншого ітератора, його швидше використовувати
list()
замістьtee()
.
В основному, tee
він розроблений для тих ситуацій, коли два (або більше) клонів одного ітератора, хоча "виходять із синхронізації" один з одним, не роблять цього багато, швидше, вони говорять в одній "околиці" ( кілька предметів позаду або попереду один одного). Не підходить для проблеми ОП "переробити з самого початку".
L = list(DictReader(...))
з іншого боку, цілком підходить до тих пір, поки список диктів може комфортно вміститися в пам'яті. Новий «ітератор з самого початку» (дуже легкий і низький накладні витрати) можна виготовити в будь-який час разом із ним iter(L)
і використовувати його частково або повністю, не впливаючи на нові чи існуючі; інші схеми доступу також легко доступні.
Як справедливо зауважено в декількох відповідях, у конкретному випадку csv
ви також можете .seek(0)
встановити базовий об'єкт файлу (досить особливий випадок). Я не впевнений, що це підтверджено документально і гарантовано, хоча це і зараз працює; Напевно, варто було б розглянути лише справді величезні файли csv, в яких list
я рекомендую, оскільки загальний підхід мав би занадто великий слід пам'яті.
list()
для кешування багатопасажу через csvreader у файлі 5 Мб, час мого виконання переходить від ~ 12 сек до ~ 0,5 сек.
Якщо у вас є файл 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. Це не спрацює, якщо ви є першим читачем на півдорозі читання файлу, оскільки ваш новий читач матиме несподівані ключі та значення, де б ви не знаходились у файлі.
Ні. Протокол ітератора Python дуже простий і містить лише один єдиний метод ( .next()
або __next__()
), і жоден метод для скидання ітератора загалом.
Загальна модель полягає в тому, щоб замість цього знову створити новий ітератор, використовуючи ту саму процедуру.
Якщо ви хочете "зберегти" ітератор, щоб ви могли повернутися до його початку, ви також можете розпакувати ітератор, використовуючи itertools.tee
csv
модуль. Сподіваємось, обидві відповіді корисні для оригінального плаката.
__iter__
. Тобто ітератори також повинні бути ітерабельними.
Так , якщо ви використовуєте 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
nditer
проїхати масив, як itertools.cycle
?
try:
next()
StopIteration
reset()
next()
Існує помилка у використанні, .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)
Це, можливо, ортогональне для початкового питання, але можна було б обернути ітератор у функції, яка повертає ітератор.
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)
Це здається, щоб уникнути кешування, яке потрібно виконати трійці (n копій) або списку (1 копія)
Для невеликих файлів ви можете скористатися more_itertools.seekable
стороннім інструментом, який пропонує скидання ітерабелів.
Демо
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()
Метод використовується для скидання / назад ітератора в 0 - ом становищі (3).
Примітка: споживання пам'яті зростає з ітерацією, тому будьте обережні, застосовуючи цей інструмент до великих файлів, як зазначено в документах .
Поки не відбувається скидання ітератора, модуль "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]
Для 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())
повертає всі залишилися значення для генератора і ефективно його скидає, якщо він не циклічний.
У мене було те саме питання і раніше. Проаналізувавши свій код, я зрозумів, що спроба скинути ітератор всередині циклів трохи збільшує часову складність, а також робить код трохи некрасивим.
Відкрийте файл і збережіть рядки до змінної в пам'яті.
# 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)
Тепер ви можете прокручувати рядки в будь-якій точці вашої області, не маючи справу з ітератором.
Одним із можливих варіантів є використання itertools.cycle()
, яке дозволить вам повторювати нескінченно без будь-яких хитрощів .seek(0)
.
iterDic = itertools.cycle(csv.DictReader(open('file.csv')))
Я приходжу до цього самого питання - хоча мені подобається tee()
рішення, я не знаю, наскільки великі будуть мої файли, і пам'ять попереджає про споживання однієї першої, ніж іншої, відштовхує мене від прийняття цього методу.
Натомість я створюю пара ітераторів, використовуючи iter()
оператори та використовую перший для мого початкового пробігу, перш ніж переходити на другий для остаточного виконання.
Отже, у випадку з читачем диктовок, якщо читач визначений за допомогою:
d = csv.DictReader(f, delimiter=",")
Я можу створити пару ітераторів із цієї "специфікації" - використовуючи:
d1, d2 = iter(d), iter(d)
Потім я можу запустити свій код 1-го проходу d1
, впевнений, що другий ітератор d2
визначений з тієї ж кореневої специфікації.
Я не випробував це вичерпно, але, здається, працює з фіктивними даними.
Тільки якщо базовий тип забезпечує механізм для цього (наприклад fp.seek(0)
).
Повернути новостворений ітератор під час останньої ітерації під час виклику 'iter ()'
class ResetIter:
def __init__(self, num):
self.num = num
self.i = -1
def __iter__(self):
if self.i == self.num-1: # here, return the new object
return self.__class__(self.num)
return self
def __next__(self):
if self.i == self.num-1:
raise StopIteration
if self.i <= self.num-1:
self.i += 1
return self.i
reset_iter = ResetRange(10)
for i in reset_iter:
print(i, end=' ')
print()
for i in reset_iter:
print(i, end=' ')
print()
for i in reset_iter:
print(i, end=' ')
Вихід:
0 1 2 3 4 5 6 7 8 9
0 1 2 3 4 5 6 7 8 9
0 1 2 3 4 5 6 7 8 9