一般来说,有没有一种有效的方法可以知道Python中的迭代器中有多少个元素,而不用遍历每个元素并计数?


当前回答

不。这是不可能的。

例子:

import random

def gen(n):
    for i in xrange(n):
        if random.randint(0, 1) == 0:
            yield i

iterator = gen(10)

迭代器的长度是未知的,直到迭代遍历它。

其他回答

不,任何方法都需要解析每个结果。你可以这样做

iter_length = len(list(iterable))

但是在无限迭代器上运行它当然不会返回。它还将消耗迭代器,如果你想使用内容,它将需要重置。

告诉我们你想要解决的真正问题可能会帮助我们找到更好的方法来实现你的实际目标。

编辑:使用list()会将整个可迭代对象一次性读入内存,这可能是不可取的。另一种方法是做

sum(1 for _ in iterable)

正如另一个人发布的那样。这样可以避免把它保存在记忆中。

我喜欢这个基数包,它是非常轻量级的,并尝试使用最快的实现,这取决于可迭代对象。

用法:

>>> import cardinality
>>> cardinality.count([1, 2, 3])
3
>>> cardinality.count(i for i in range(500))
500
>>> def gen():
...     yield 'hello'
...     yield 'world'
>>> cardinality.count(gen())
2

count()的实际实现如下:

def count(iterable):
    if hasattr(iterable, '__len__'):
        return len(iterable)

    d = collections.deque(enumerate(iterable, 1), maxlen=1)
    return d[0][0] if d else 0

通常的做法是将这类信息放在文件头中,并让pysam允许您访问这些信息。我不知道格式,但是你检查过API了吗?

正如其他人所说,你不能从迭代器中知道长度。

这段代码应该工作:

>>> iter = (i for i in range(50))
>>> sum(1 for _ in iter)
50

尽管它确实遍历每一项并计算它们,但这是最快的方法。

它也适用于迭代器中没有项的情况:

>>> sum(1 for _ in range(0))
0

当然,对于一个无限的输入,它会一直运行,所以请记住迭代器可以是无限的:

>>> sum(1 for _ in itertools.count())
[nothing happens, forever]

此外,请注意,这样做将耗尽迭代器,并且进一步尝试使用它将看不到任何元素。这是Python迭代器设计的一个不可避免的结果。如果你想保留元素,你就必须把它们存储在一个列表或其他东西中。

有点。你可以检查__length_hint__方法,但要注意(至少在Python 3.4之前,正如gsnedders所指出的那样),它是一个未记录的实现细节(在线程中跟随消息),它很可能消失或召唤鼻子恶魔。

否则,没有。迭代器只是一个只公开next()方法的对象。你可以根据需要多次调用它,它们最终可能引发也可能不会引发StopIteration。幸运的是,大多数时候这种行为对编码器来说是透明的。:)