【问题标题】:Why can't I iterate twice over the same data?为什么我不能对相同的数据进行两次迭代?
【发布时间】:2014-10-09 19:08:30
【问题描述】:

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

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")

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

我第一次迭代数据工作正常,但第二次运行最后一个列表“for row in data”时,这没有返回任何内容......所以执行一次有效但不是两次......?

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

【问题讨论】:

标签: python iterator


【解决方案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 = list(db[3])

但如果有很多元素,最好使用tee() 创建独立的迭代器:

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

现在我们可以依次循环遍历每一个:

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

for e in it2:
    print("doing this two times")

【讨论】:

  • it.next() 非常适合这个示例,因为您可以想象 next 方法正在修改 it 以某种方式跟踪已产生的内容(当然,对于很多对象,这种“跟踪”或找出下一个要返回的值发生在 C 代码中)。但是,通常在编写代码时,最好使用next(it),前提是您不需要支持真正旧的python版本——这与python3.x向前兼容,其中it.next()被重命名为it.__next__()...跨度>
  • @ÓscarLópez -- 实际上我并没有试图暗示你应该改变答案。我确实有点喜欢it.next() 使it 可能正在改变的关联。 next(it) 不太清楚,因为它返回 something 并且 it 被修改(这通常不是 python 中最直观的东西......)
  • @ÓscarLópez 来自tee 文档的注释:“此迭代工具可能需要大量辅助存储(取决于需要存储多少临时数据)。通常,如果一个迭代器使用大部分或全部在另一个迭代器开始之前的数据,使用 list() 而不是 tee() 更快。"因此,如果您像示例中那样使用it1it2,您可能无法从tee 中获得任何真正的好处(同时可能会产生一些额外的开销)。
  • 我支持@svk - 在这种情况下tee 将创建迭代器值的完整副本,其效率略低于单个list 调用。当可迭代元素有很多时,不应该使用tee - 这不相关,但是当存在使用局部性时 - 在这种情况下,tee 的缓存可能小于整个列表。例如,如果两个迭代器并驾齐驱,例如在 zip(a, islice(b, 1)) 调用中。
【解决方案2】:

我想为那些在2017年寻找解决方案并使用python 2.7或3的人完成@ÓscarLópez的答案。

方法 tee() 现在不接受关键字参数,并等待第二个参数是整数,而不是关键字。这才是tee()的正确使用方式:

import itertools
it1, it2 = itertools.tee(db[3], 2)

【讨论】:

    【解决方案3】:

    一旦迭代器用完,它就不会再产生了。

    >>> it = iter([3, 1, 2])
    >>> for x in it: print(x)
    ...
    3
    1
    2
    >>> for x in it: print(x)
    ...
    >>>
    

    【讨论】:

    • 有道理,但我该如何解决呢?
    • @JSchwartz,将迭代器转换为序列对象(listtuple)。然后迭代序列对象。 (仅当 csv 的大小不是很大时)
    • @JSchwartz,或者,如果您可以访问底层文件对象并且是可搜索的。您可以在第二个循环之前更改文件位置:csv_file_object.seek(0)
    猜你喜欢
    • 2023-01-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-07
    相关资源
    最近更新 更多