考虑代码:
def test(data):
for row in data:
print("first loop")
for row in data:
print("second loop")
当data
是一个迭代器时,例如列表迭代器或生成器表达式 *,这是不起作用的:
>>> test(iter([1, 2]))
first loop
first loop
>>> test((_ for _ in [1, 2]))
first loop
first loop
这会打印first loop
几次,因为data
不是空的。但是,它 * 不 * 打印second loop
。为什么在data
上迭代第一次可以工作,但第二次不行?我怎样才能让它第二次工作?
除了for
循环之外,任何类型的迭代都会出现同样的问题:列表/集合/字典解析,将迭代器传递给list()
、sum()
或reduce()
等。
另一方面,如果data
是另一种可迭代对象,比如list
或range
(都是序列),则两个循环都按预期运行:
>>> test([1, 2])
first loop
first loop
second loop
second loop
>>> test(range(2))
first loop
first loop
second loop
second loop
- 更多示例:
- file objects
- generators created from an explicit generator function
filter
、map
和zip
对象(在3.x中)enumerate
objectscsv.reader
sitertools
标准库中定义的各种迭代器
有关一般理论和术语解释,请参见What are iterator, iterable, and iteration?。
要 * 检测 * 输入是迭代器还是“可重用”的可迭代对象,请参见Ensure that an argument can be iterated twice。
5条答案
按热度按时间hec6srdp1#
迭代器只能使用一次。例如:
当迭代器被提供给
for
循环时,最后一个StopIteration
将导致它第一次退出,而试图在另一个for循环中使用相同的迭代器将立即再次导致StopIteration
,因为迭代器已经被使用。解决这个问题的一个简单方法是将所有元素保存到一个列表中,可以根据需要多次遍历该列表。
但是,如果迭代器要迭代许多元素,那么使用
tee()
创建独立的迭代器是一个更好的主意:现在可以依次迭代每一个:
rvpgvaaj2#
迭代器(例如调用
iter
、生成器表达式或yield
的生成器函数)是有状态的,只能使用一次。Óscar López's answer中对此进行了解释,但是,该答案出于性能原因建议使用
itertools.tee(data)
而不是list(data)
,这是一种误导。在大多数情况下,当您希望迭代整个data
,然后再次迭代整个data
时,tee
比简单地将整个迭代器消耗到一个列表中,然后在其上迭代两次要花费更多的时间和使用更多的内存。如果只使用每个迭代器的前几个元素,或者交替使用一个迭代器的几个元素和另一个迭代器的几个元素,则tee
可能是首选。l2osamch3#
一旦迭代器耗尽,它就不会再生成任何东西。
mbjcgjjk4#
如何在迭代器上循环两次?
这是不可能的!(稍后解释。)相反,请执行以下操作之一:
缺点:这会消耗内存。
缺点:迭代本身可能是昂贵的(例如从磁盘或网络读取)。
缺点:大多数迭代器不能"重置"。
Iterator
的哲学世界分为两类:
如果我们要定义一个序列迭代器,它可能看起来像这样:
这里重要的是,* 通常 *,迭代器本身并不存储任何实际的数据。
迭代器通常是一个临时的数据流模型。这个数据源被迭代过程所消耗。这是一个很好的提示,说明了为什么一个任意的数据源不能循环超过一次。我们需要打开一个新的临时数据流(即创建一个新的迭代器)来做到这一点。
正在耗尽
Iterator
当我们从一个迭代器中提取元素时,从迭代器的当前元素开始,一直持续到它完全耗尽,这就是
for
循环的作用:让我们通过告诉
for
循环如何提取next
项来支持SequenceIterator
中的此功能:等等,如果
index
超过了items
的最后一个元素,我们应该为此引发一个 * safe * 异常:现在,for循环知道何时停止从迭代器中提取项。
如果我们现在尝试再次遍历迭代器会发生什么?
由于第二个循环从当前的
iterator.index
(值3)开始,它没有其他要打印的内容,因此iterator.__next__
引发StopIteration
异常,导致循环立即结束。lh80um4z5#
尽可能不要在for循环中首先使用迭代器,这是不必要的,使用可迭代对象,iter(iterable)是python在执行for循环时调用的,一个可迭代对象可以循环多次。
如果你正在写代码,你发现你自己试图迭代一个迭代器,你的代码中一定有一些不自然的地方,试着先修复它。