可以在Python中重置迭代器吗?

112 python iterator generator

我可以在Python中重置迭代器/生成器吗?我正在使用DictReader并希望将其重置(从csv模块)到文件的开头.

Ale*_*lli 74

我看到许多回答暗示了itertools.tee,但这忽略了文档中的一个重要警告:

这个itertool可能需要大量的辅助存储(取决于需要存储多少临时数据).一般来说,如果一个迭代器使用了大部分或全部数据的另一迭代开始前,它是更快地使用list()替代tee().

基本上,tee是专为那些情况,其中两个(或更多)一个迭代器的克隆,而"不同步的"相互,不这样做的多 -相反,他们在相同的"附近"说(一彼此背后或之前的几个项目).不适合OP的"从一开始就重做"的问题.

L = list(DictReader(...))另一方面,只要序列表可以很好地适应记忆,这是非常合适的.一个新的"迭代器从一开始"(非常轻量级和低开销)可以随时iter(L)使用,部分或全部使用而不影响新的或现有的; 其他访问模式也很容易获得.

正如几个答案正确地指出的那样,在特定情况下csv你也可以.seek(0)使用底层文件对象(一个相当特殊的情况).我不确定它是否有记录和保证,但它目前有效; 它可能值得考虑仅用于真正巨大的csv文件,其中list我推荐作为一般方法将具有太大的内存占用.

  • 使用`list()`在5MB文件上缓存csvreader上的多通道,我的运行时间从~12secs到~0.5s. (5认同)

Wil*_*uck 30

如果你有一个名为'blah.csv'的csv文件看起来像

a,b,c,d
1,2,3,4
2,3,4,5
3,4,5,6
Run Code Online (Sandbox Code Playgroud)

你知道你可以打开文件进行阅读,并创建一个DictReader

blah = open('blah.csv', 'r')
reader= csv.DictReader(blah)
Run Code Online (Sandbox Code Playgroud)

然后,您将能够获得下一行reader.next(),该行应该输出

{'a':1,'b':2,'c':3,'d':4}
Run Code Online (Sandbox Code Playgroud)

再次使用它会产生

{'a':2,'b':3,'c':4,'d':5}
Run Code Online (Sandbox Code Playgroud)

但是,此时如果您使用blah.seek(0),下次打电话时reader.next()您将获得

{'a':1,'b':2,'c':3,'d':4}
Run Code Online (Sandbox Code Playgroud)

再次.

这似乎是您正在寻找的功能.我确信这种方法有一些与我不了解的技巧有关.@Brian建议简单地创建另一个DictReader.如果您是第一个阅读器在读取文件的一半时,这将无法工作,因为您的新阅读器将具有来自文件中的任何位置的意外键和值.


u0b*_*6ae 22

不.Python的迭代器协议非常简单,只提供一个方法(.next()__next__()),而且通常没有方法来重置迭代器.

常见的模式是再次使用相同的过程创建一个新的迭代器.

如果你想"保存"一个迭代器,以便你可以回到它的开头,你也可以通过使用fork iterator itertools.tee

  • @Wilduck:我明白你的回答。我刚刚回答了迭代器问题,我对 `csv` 模块一无所知。希望这两个答案对原始海报有用。 (2认同)

Ste*_*ski 11

使用上面的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)
Run Code Online (Sandbox Code Playgroud)


Dev*_*per 10

是的,如果你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
Run Code Online (Sandbox Code Playgroud)


Ani*_*ish 7

这可能与原始问题正交,但可以将迭代器包装在一个返回迭代器的函数中。

def get_iter():
    return iterator
Run Code Online (Sandbox Code Playgroud)

要重置迭代器,只需再次调用该函数。如果该函数没有参数时,这当然是微不足道的。

在函数需要一些参数的情况下,使用 functools.partial 创建一个可以传递的闭包,而不是原始迭代器。

def get_iter(arg1, arg2):
   return iterator
from functools import partial
iter_clos = partial(get_iter, a1, a2)
Run Code Online (Sandbox Code Playgroud)

这似乎避免了 tee (n 副本) 或列表 (1 副本) 需要做的缓存