为什么我不能在同一数据上迭代两次?

JSc*_*rtz 34 python

老实说,我在这里有点困惑,为什么我不能在相同的数据上迭代两次?

def _view(self,dbName):
    db = self.dictDatabases[dbName]
    data = db[3]

    for row in data:
        print("doing this one time")

    for row in data:
        print("doing this two times")
Run Code Online (Sandbox Code Playgroud)

这将打印出"一次这样做"几次(因为数据有几行),但它根本不会打印出"这样做两次"......

我第一次迭代数据工作正常,但第二次当我运行最后一个列表"for data in data"时,这没有返回...所以执行它一次工作但不是两次......?

仅供参考 - 数据是一个csv.reader对象(如果是这样的原因)......

Ósc*_*pez 36

这是因为它data是一个迭代器,你只能使用一次迭代器.例如:

lst = [1, 2, 3]
it = iter(lst)

next(it)
=> 1
next(it)
=> 2
next(it)
=> 3
next(it)
=> StopIteration
Run Code Online (Sandbox Code Playgroud)

如果我们使用for循环遍历某些数据,那么最后一次StopIteration将导致它第一次退出.如果我们试图遍历它再次,我们会不断收到StopIteration异常,因为迭代器已经被消耗掉.

现在的第二个问题:如果我们需要遍历迭代不止一次?一个简单的解决方案是创建一个包含元素的列表,我们可以根据需要多次遍历它.只要列表中的元素很少,这样就可以了:

data = list(db[3])
Run Code Online (Sandbox Code Playgroud)

但是如果有很多元素,那么使用tee()以下方法创建独立迭代器是一个更好的主意:

import itertools
it1, it2 = itertools.tee(db[3], n=2) # create as many as needed
Run Code Online (Sandbox Code Playgroud)

现在我们可以轮流遍历每一个:

for e in it1:
    print("doing this one time")

for e in it2:
    print("doing this two times")
Run Code Online (Sandbox Code Playgroud)

  • 我支持 @svk - 在这种情况下,“tee”将以比单个“list”调用稍微低效的方式创建迭代器值的完整副本。当可迭代中有很多元素时,不应该使用“tee”——这不相关,但是当存在使用局部性时——在这种情况下,“tee”的缓存可能小于整个列表。例如,如果两个迭代器并驾齐驱,就像在“zip(a, islice(b, 1))”调用中一样。 (14认同)
  • @user2357112supportsMonica 您对此答案的编辑正在[meta](https://meta.stackoverflow.com/questions/416012)上讨论。 (13认同)
  • @ÓscarLópez在`tee`文档中的注释:“此itertool可能需要大量辅助存储(取决于需要存储多少临时数据)。通常,如果一个迭代器在另一个迭代器启动之前使用了大部分或全部数据,使用list()代替tee()更快。” 因此,如果像示例中那样使用“ it1”和“ it2”,则可能不会从“ tee”中获得任何实际好处(尽管可能会花费一些额外的开销)。 (2认同)

kay*_*ya3 35

迭代器(例如,来自调用iter、来自生成器表达式或来自生成器函数yield)是有状态的并且只能使用一次。

\n

\xc3\x93scar L\xc3\xb3pez\ 的答案对此进行了解释,但是,该答案建议出于性能原因而使用itertools.tee(data)而不是使用list(data)是具有误导性的。\n在大多数情况下,您想要迭代整个data然后再次迭代整个迭代器,这tee比简单地将整个迭代器消耗到列表中然后迭代两次要花费更多的时间和使用更多的内存。根据文档

\n
\n

这个 itertool 可能需要大量的辅助存储(取决于需要存储多少临时数据)。一般来说,如果一个迭代器在另一个迭代器启动之前使用了大部分或全部数据,则list()使用tee().

\n
\n

tee如果您只消耗每个迭代器的前几个元素,或者您将交替消耗一个迭代器中的一些元素和另一个迭代器中的一些元素,则可能是首选。

\n


Mat*_*haq 11

如何让迭代器循环两次?

这通常是不可能的。(稍后解释。)相反,请执行以下操作之一:

  • 将迭代器收集为可以多次循环的东西。

    items = list(iterator)
    
    for item in items:
        ...
    
    Run Code Online (Sandbox Code Playgroud)

    缺点:这会消耗内存。

  • 创建一个新的迭代器。创建一个新的迭代器通常只需要一微秒。

    for item in create_iterator():
        ...
    
    for item in create_iterator():
        ...
    
    Run Code Online (Sandbox Code Playgroud)

    缺点:迭代本身可能很昂贵(例如从磁盘或网络读取)。

  • 重置“迭代器”。例如,使用文件迭代器:

    with open(...) as f:
        for item in f:
            ...
    
        f.seek(0)
    
        for item in f:
            ...
    
    Run Code Online (Sandbox Code Playgroud)

    缺点:大多数迭代器无法“重置”。


哲学Iterator

通常,尽管技术上不是1

  • Iterable:表示数据的可循环对象。例子:listtuplestr
  • 迭代器:指向可迭代对象的某个元素的指针。

如果我们要定义一个序列迭代器,它可能看起来像这样:

class SequenceIterator:
    index: int
    items: Sequence  # Sequences can be randomly indexed via items[index].

    def __next__(self):
        """Increment index, and return the latest item."""
Run Code Online (Sandbox Code Playgroud)

这里重要的是迭代器通常不会在其内部存储任何实际数据。

迭代器通常对临时数据“流”进行建模。该数据源由迭代过程消耗。这是一个很好的提示,说明为什么不能多次循环任意数据源。我们需要打开一个新的临时数据流(即创建一个新的迭代器)来做到这一点。

筋疲力尽Iterator

当我们从迭代器中提取项目时,从迭代器的当前元素开始,一直持续到完全耗尽,会发生什么?这就是for循环的作用:

iterable = "ABC"
iterator = iter(iterable)

for item in iterator:
    print(item)
Run Code Online (Sandbox Code Playgroud)

让我们SequenceIterator通过告诉for循环如何提取next项目来支持此功能:

class SequenceIterator:
    def __next__(self):
        item = self.items[self.index]
        self.index += 1
        return item
Run Code Online (Sandbox Code Playgroud)

坚持,稍等。如果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
Run Code Online (Sandbox Code Playgroud)

现在,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
Run Code Online (Sandbox Code Playgroud)

由于第二个循环从 current iterator.index(即 3)开始,因此它没有任何其他可打印的内容,因此iterator.__next__引发StopIteration异常,导致循环立即结束。


1 技术上:

  • Iterable:__iter__调用时返回迭代器的对象。
  • 迭代器:一种可以__next__在循环中重复调用以提取项目的对象。此外,调用__iter__它应该返回它self

更多详细信息请参见此处


fal*_*tru 9

一旦迭代器耗尽,它将不再产生.

>>> it = iter([3, 1, 2])
>>> for x in it: print(x)
...
3
1
2
>>> for x in it: print(x)
...
>>>
Run Code Online (Sandbox Code Playgroud)

  • 这是有道理的,但我该如何解决呢? (3认同)
  • @JSchwartz,或者,如果你可以访问底层文件对象,那是可以搜索的.你可以在第二个循环之前改变文件位置:`csv_file_object.seek(0)` (2认同)

归档时间:

查看次数:

16130 次

最近记录:

6 年,4 月 前