Python - 没有消耗的迭代器中的计数元素

时间:2017-04-13 20:07:11

标签: 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函数,以便它以这种方式运行吗?即使问题的答案是“无法做到”,这仍然是一个完全有效的答案。它不会让问题变得糟糕。并且证明它是不可能的将远非微不足道......

4 个答案:

答案 0 :(得分:5)

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

答案 1 :(得分:2)

获取仲裁迭代器长度的唯一方法是迭代它,所以这里的基本问题是定义不明确的。你无法获得任何迭代器的长度而无需迭代它。

迭代器本身也可以在迭代时更改它的内容,因此计数可能不会是常量。

但是有可能做你要求的事情,要警告他们没有一个是万无一失或真正有效:

当使用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并使用this.router.navigate(['component2']); 效率低(内存和速度)。

答案 2 :(得分:1)

没有通用的方法可以做你想做的事。迭代器可能没有明确定义的长度(例如永远迭代的itertools.count)。或者它可能有一个预先计算成本很高的长度,因此它不会让你知道你到达目的地要走多远(例如一个文件对象,可以迭代产生线,这不是无需读取整个文件的内容即可轻松计算。)

某些类型的迭代器可能会实现一个返回估计长度的__length_hint__方法,但该长度可能不准确。并非所有迭代器都会实现该方法,因此您可能无法依赖它(它对列表迭代器起作用,但对许多其他迭代器不起作用)。

处理迭代器的整个内容的最佳方法通常是将其转储到列表或其他容器中。在完成所需的任何操作(比如在其上调用len)之后,您可以再次遍历列表。显然这需要迭代器是有限的(并且它的所有内容都适合内存),但这是你必须处理的限制。

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

答案 3 :(得分:1)

我无法提出一个确切的解决方案(因为迭代器可能是不可变类型),但这是我最好的尝试。根据{{​​3}}(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]