可以在Python中重置迭代器吗?
我可以在Python中重置迭代器/生成器吗? 我正在使用DictReader并希望将其重置(从csv模块)到文件的开头。
13个解决方案
68 votes
我看到许多回答暗示了itertools.tee,但是它忽略了文档中的一个重要警告:
这个itertool可能需要很多 辅助存储(取决于如何 需要很多临时数据 存储)。 一般来说,如果一个迭代器 之前使用大部分或全部数据 另一个迭代器启动,它更快 使用list()而不是csv。
基本上,csv是针对一个迭代器的两个(或更多)克隆,而#34;不同步的情况而设计的。 相互之间不要这么做 - 相反,他们会在同一个地方说#34;附近" (一些项目背后或相互提前)。 不适合OP从一开始就重做的问题"重做。
另一方面,csv非常适合,只要序列表可以很好地适应内存。 一个新的"迭代器从一开始" (非常轻量级,低开销)可随时用.seek(0)制作,部分或全部使用而不影响新的或现有的; 其他访问模式也很容易获得。
正如几个答案正确地说,在csv的具体情况下,您也可以.seek(0)底层文件对象(一个相当特殊的情况)。 我不确定这些文件是否有记录和保证,尽管它目前有效; 它可能值得考虑仅用于真正巨大的csv文件,其中list我推荐作为一般方法将具有太大的内存占用。
Alex Martelli answered 2019-07-24T07:35:10Z
29 votes
如果您有一个名为' blah.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()时,您将获得
{'a':1,'b':2,'c':3,'d':4}
再次。
这似乎是您正在寻找的功能。 我确定有一些与这种方法相关的技巧,但我并不知道。 @Brian建议简单地创建另一个DictReader。 如果您的第一个读者在读取文件的一半时,这将无法工作,因为您的新读者将从文件中的任何位置获得意外的键和值。
Wilduck answered 2019-07-24T07:36:19Z
21 votes
不可以.Python的迭代器协议非常简单,只提供一种方法(itertools.tee或__next__()),并且通常没有方法来重置迭代器。
常见的模式是再次使用相同的过程创建一个新的迭代器。
如果你想"保存" 一个迭代器,以便你可以回到它的开头,你也可以通过使用itertools.tee来分叉迭代器
u0b34a0f6ae answered 2019-07-24T07:37:00Z
10 votes
使用上面的Alex Martelli和Wilduck所倡导的.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)
Steven Rumbalski answered 2019-07-24T07:37:34Z
10 votes
是的,如果您使用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
Developer answered 2019-07-24T07:38:02Z
3 votes
这可能与原始问题正交,但可以将迭代器包装在返回迭代器的函数中。
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份)需要执行的缓存
Anish answered 2019-07-24T07:38:50Z
2 votes
虽然没有迭代器重置,但是#34; itertools" 来自python 2.6(及更高版本)的模块有一些可以帮助的实用程序。其中之一就是" tee" 它可以生成迭代器的多个副本,并缓存前面运行的结果,以便在副本上使用这些结果。 我会把你的目的:
>>> 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]
jsbueno answered 2019-07-24T07:39:20Z
1 votes
对于小文件,您可以考虑使用DictReader - 提供重置可迭代的第三方工具。
演示
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包含在seekable对象(1)和高级(2)中。 seek()方法用于将迭代器重置/倒回到第0位置(3)。
注意:内存消耗随着迭代而增长,因此请谨慎地将此工具应用于大型文件,如文档中所示。
pylang answered 2019-07-24T07:40:07Z
1 votes
问题
我之前遇到过同样的问题。 在分析了我的代码之后,我意识到尝试重置循环内部的迭代器会稍微增加时间复杂度,这也会使代码变得有点丑陋。
解
打开文件并将行保存到内存中的变量。
# 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)
现在,您可以在范围内的任何位置循环遍历行,而无需处理迭代器。
Anthony Holloman answered 2019-07-24T07:40:53Z
0 votes
仅当基础类型提供了这样做的机制时(例如fp.seek(0))。
Ignacio Vazquez-Abrams answered 2019-07-24T07:41:21Z
0 votes
对于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()
mAsT3RpEE answered 2019-07-24T07:41:54Z
0 votes
list(generator())返回生成器的所有剩余值,如果没有循环,则会有效地重置它。
Theoremiser answered 2019-07-24T07:42:22Z
0 votes
可能的选择是使用itertools.cycle(),它允许无限期迭代而不需要像.seek(0)这样的技巧
iterDic = itertools.cycle(csv.DictReader(open('file.csv')))
Greg H answered 2019-07-24T07:42:50Z