Python - 在不消耗的情况下在迭代器中计算元素



给定一个迭代器it,我想要一个函数it_count,它返回迭代器产生的元素计数,而不会破坏迭代器。 例如:

ita = iter([1, 2, 3])
print(it_count(ita))
print(it_count(ita))

应该打印

3
3

有人指出,对于所有迭代器来说,这可能不是一个定义明确的问题,所以我不是在寻找一个完全通用的解决方案,但它应该按照给定示例的预期运行。


好的,让我进一步澄清我的具体情况。 给定以下代码:

ita = iter([1, 2, 3])
itb, itc = itertools.tee(ita)
print(sum(1 for _ in itb))
print(sum(1 for _ in itc))

。我们可以编写上面描述的it_count函数,以便它以这种方式运行吗? 即使问题的答案是"这不可能做到",这仍然是一个完全有效的答案。 这并没有使问题变得糟糕。 而这是不可能的证据远非微不足道......

不可能。在迭代器完全消耗之前,它没有具体的元素计数。

获取任意迭代器长度的唯一方法是遍历它,因此这里的基本问题定义不明确。如果不迭代任何迭代器,就无法获取它的长度。

此外,迭代器本身可能会在迭代时更改其内容,因此计数可能不是恒定的。


但是有一些可能性可能会按照你的要求去做,请注意,它们都不是万无一失或真正有效的:

使用 python 3.4 或更高版本时,您可以使用operator.length_hint并希望迭代器支持它(请注意:没有多少迭代器这样做!这只是一个提示,实际长度可能会有所不同!

>>> from operator import length_hint
>>> it_count = length_hint
>>> ita = iter([1, 2, 3])
>>> print(it_count(ita))
3
>>> print(it_count(ita))
3

作为替代方案:您可以使用itertools.tee但在使用它之前请仔细阅读其文档。它可能会解决您的问题,但不会真正解决根本问题。

import itertools
def it_count(iterator):
return sum(1 for _ in iterator)
ita = iter([1, 2, 3])
it1, it2 = itertools.tee(ita, 2)
print(it_count(it1))  # 3
print(it_count(it2))  # 3

但这比将其转换为list并在其上使用len效率(内存和速度)要低。

我无法提出确切的解决方案(因为迭代器可能是不可变的类型),但这是我最好的尝试。 根据文档(itertools.tee的最后一段),我相信第二个应该更快)。

选项 1

def it_count(it):
tmp_it, new_it = itertools.tee(it)
return sum(1 for _ in tmp_it), new_it

选项 2

def it_count2(it):
lst = list(it)
return len(lst), lst

它运行良好,但有一点烦恼,即返回货币对而不是简单的计数。

ita = iter([1, 2, 3])
count, ita = it_count(ita)
print(count)
Output: 3
count, ita = it_count2(ita)
print(count)
Output: 3
count, ita = it_count(ita)
print(count)
Output: 3
print(list(ita))
Output: [1, 2, 3]

没有通用的方法可以做你想做的事情。迭代器可能没有明确定义的长度(例如itertools.count永远迭代)。或者它的长度可能很昂贵,所以它不会让你知道你必须走多远,直到你到达终点(例如,一个文件对象,它可以迭代产生行,如果不读取整个文件的内容,就不容易计算

)。某些类型的迭代器可能会实现返回估计长度的__length_hint__方法,但该长度可能不准确。并不是所有的迭代器都会实现这种方法,所以你可能不能依赖它(它确实适用于列表迭代器,但不适用于许多其他迭代器)。

通常,处理迭代器全部内容的最佳方法是将其转储到列表或其他容器中。完成所需的任何操作(例如调用len)后,可以再次循环访问列表。显然,这要求迭代器是有限的(并且其所有内容都适合内存),但这是您必须处理的限制。

如果你只需要提前看几个元素,你可以使用itertools.tee,但如果你需要消耗整个内容,它并不比转储到列表中更好(因为它保留了一个返回的迭代器看到的值,但另一个在类似于deque的数据结构中)。查找迭代器的长度没有任何用处。

最新更新