为啥我不能对相同的数据进行两次迭代?
Posted
技术标签:
【中文标题】为啥我不能对相同的数据进行两次迭代?【英文标题】:Why can't I iterate twice over the same data?为什么我不能对相同的数据进行两次迭代? 【发布时间】:2014-08-16 03:42:47 【问题描述】:为什么我不能在同一个iterator 上迭代两次?
# data is an iterator.
for row in data:
print("doing this one time")
for row in data:
print("doing this two times")
这会打印几次"doing this one time"
,因为data
是非空的。但是,它不打印"doing this two times"
。为什么第一次迭代 data
可以工作,但第二次不行?
【问题讨论】:
可迭代与迭代器。 我并不是说这是重复的,但您可能还想参考***.com/questions/9884132/… 以获得更多上下文/解释 相关:Resetting an iterator object 此问题中提供的代码并不是重现问题的最短时间。可以通过提供更好的代码示例来改进这个问题。 @Trilarion 是的,我认为def _view(self,dbName): db = self.dictDatabases[dbName] data = db[3]
可以安全删除,因为没有其他答案讨论该部分代码。
【参考方案1】:
这是因为data
是一个迭代器,一个迭代器只能使用一次。例如:
lst = [1, 2, 3]
it = iter(lst)
next(it)
=> 1
next(it)
=> 2
next(it)
=> 3
next(it)
=> StopIteration
如果我们使用for
循环遍历一些数据,最后一个StopIteration
将导致它第一次退出。如果我们尝试再次对其进行迭代,我们将不断收到StopIteration
异常,因为迭代器已被消耗。
现在是第二个问题:如果我们确实需要多次遍历迭代器怎么办?一个简单的解决方案是将所有元素保存到一个列表中,可以根据需要多次遍历该列表。例如,如果 data
是一个迭代器:
data = list(data)
只要列表中的元素很少就可以。但是,如果有很多元素,最好使用tee()
创建独立的迭代器:
import itertools
it1, it2 = itertools.tee(data, 2) # create as many as needed
现在我们可以依次循环遍历每一个:
for e in it1:
print("doing this one time")
for e in it2:
print("doing this two times")
【讨论】:
@ÓscarLópez 来自tee
文档的注释:“此迭代工具可能需要大量辅助存储(取决于需要存储多少临时数据)。一般来说,如果一个迭代器使用大部分或全部在另一个迭代器开始之前的数据,使用 list() 而不是 tee() 更快。"因此,如果您像示例中一样使用it1
和it2
,您可能无法从tee
中获得任何真正的好处(同时可能需要一些额外的开销)。
我支持@svk - 在这种情况下tee
将创建迭代器值的完整副本,其效率略低于单个list
调用。当可迭代元素有很多时,不应该使用tee
- 这不相关,但是当存在使用局部性时 - 在这种情况下,tee
的缓存可能小于整个列表。例如,如果两个迭代器并驾齐驱,例如在zip(a, islice(b, 1))
调用中。
@user2357112supportsMonica 您对此答案的编辑正在meta 上讨论。【参考方案2】:
迭代器(例如来自调用iter
、来自生成器表达式或来自yield
的生成器函数)是有状态的,并且只能使用一次,如Óscar López's answer 中所述。但是,出于性能原因,该答案建议使用itertools.tee(data)
而不是list(data)
具有误导性。
在大多数情况下,如果您想遍历整个 data
然后再次遍历整个它,tee
比简单地将整个迭代器消耗到一个列表中然后迭代它两次。如果您只使用每个迭代器的前几个元素,或者如果您将交替使用来自一个迭代器的几个元素,然后使用另一个迭代器的几个元素,tee
可能是首选。
【讨论】:
应该可能只链接到哈希(只是#25336738
),因为需要重新加载整个页面是很糟糕的
@somebody 我不认为这是可能的 - 至少,我试过了,Stack Overflow 的编辑器并没有把它变成一个链接。如果您知道该怎么做,那么我建议您进行编辑。
:( 不幸的是,这似乎不可能(请参阅this feature request) - 似乎下一个最好的事情是假设用户点击了问题(而不是其中一个答案):@ 987654323@【参考方案3】:
一旦迭代器用完,它就不会再产生了。
>>> it = iter([3, 1, 2])
>>> for x in it: print(x)
...
3
1
2
>>> for x in it: print(x)
...
>>>
【讨论】:
有道理,但我该如何解决呢? @JSchwartz,将迭代器转换为序列对象(list
,tuple
)。然后迭代序列对象。 (仅当 csv 的大小不是很大时)
@JSchwartz,或者,如果您可以访问底层文件对象并且是可搜索的。您可以在第二个循环之前更改文件位置:csv_file_object.seek(0)
【参考方案4】:
如何循环遍历一个迭代器两次?
这是不可能的! (稍后解释。)改为:
将迭代器收集成一个可以循环多次的东西。
items = list(iterator)
for item in items:
...
缺点:这会消耗内存。
创建一个新的迭代器。创建一个新的迭代器通常只需要一微秒。
for item in create_iterator():
...
for item in create_iterator():
...
缺点:迭代本身可能很昂贵(例如从磁盘或网络读取)。
重置“迭代器”。例如,使用文件迭代器:
with open(...) as f:
for item in f:
...
f.seek(0)
for item in f:
...
缺点:大多数迭代器无法“重置”。
Iterator
的哲学
世界分为两类:
Iterable: 用于保存数据的 for-loopable 数据结构。示例:list
、tuple
、str
。
迭代器:指向可迭代对象的某个元素的指针。
如果我们要定义一个序列迭代器,它可能看起来像这样:
class SequenceIterator:
index: int
items: Sequence # Sequences can be randomly indexed via items[index].
def __next__(self):
"""Increment index, and return the latest item."""
这里重要的是通常,迭代器不会在自身内部存储任何实际数据。
迭代器通常对数据的临时“流”进行建模。该数据源由迭代的过程使用。这是一个很好的提示,说明为什么不能多次循环遍历任意数据源。为此,我们需要打开一个新的临时数据流(即创建一个新的迭代器)。
耗尽Iterator
当我们从迭代器中提取项目时会发生什么,从迭代器的当前元素开始,一直持续到它完全耗尽?这就是 for
循环的作用:
iterable = "ABC"
iterator = iter(iterable)
for item in iterator:
print(item)
让我们通过告诉for
循环如何提取next
项目来支持SequenceIterator
中的此功能:
class SequenceIterator:
def __next__(self):
item = self.items[self.index]
self.index += 1
return item
等一下。如果index
超过items
的最后一个元素怎么办?我们应该为此提出一个安全异常:
class SequenceIterator:
def __next__(self):
try:
item = self.items[self.index]
except IndexError:
raise StopIteration # Safely says, "no more items in iterator!"
self.index += 1
return item
现在,for 循环知道何时停止从迭代器中提取项目。
如果我们现在再次尝试循环遍历迭代器会发生什么?
iterable = "ABC"
iterator = iter(iterable)
# iterator.index == 0
for item in iterator:
print(item)
# iterator.index == 3
for item in iterator:
print(item)
# iterator.index == 3
由于第二个循环从当前的 iterator.index
开始,即 3,它没有其他要打印的内容,因此 iterator.__next__
引发 StopIteration
异常,导致循环立即结束。
【讨论】:
以上是关于为啥我不能对相同的数据进行两次迭代?的主要内容,如果未能解决你的问题,请参考以下文章
为啥我似乎不能在 python pymysql 中迭代地填充一个 sql 表?
为啥不能两次获取相同的数据? NSFetchedResultsController 为空