在Python中获取迭代器中元素的数量
有没有一种有效的方法来了解Python中的迭代器中有多less元素,一般来说,不需要遍历每一个元素并计数呢?
不,这是不可能的。
例:
import random def gen(n): for i in xrange(n): if random.randint(0, 1) == 0: yield i iterator = gen(10)
iterator
长度是未知的,直到你遍历它。
这个代码应该工作:
>>> iter = (i for i in range(50)) >>> sum(1 for _ in iter) 50
虽然它遍历每个项目并对它们进行计数,但它是最快的方法。
不,任何方法都需要你解决每一个结果。 你可以做
iter_length = len(list(iterable))
但是在一个无限的迭代器上运行它当然不会返回。 它也会消耗迭代器,如果你想使用内容,它将需要重置。
告诉我们你想要解决的问题可能会帮助我们find一个更好的方法来实现你的实际目标。
编辑:使用list()
将一次读取整个迭代到内存中,这可能是不可取的。 另一种方法是做
sum(1 for _ in iterable)
作为另一个人张贴。 这将避免保持在记忆中。
你不能(除了一个特定的迭代器types实现一些特定的方法,使其成为可能)。
一般来说,你只能通过使用迭代器来计算迭代器项目。 可能是最有效的方法之一:
import itertools from collections import deque def count_iter_items(iterable): """ Consume an iterable not reading it into memory; return the number of items. """ counter = itertools.count() deque(itertools.izip(iterable, counter), maxlen=0) # (consume at C speed) return next(counter)
(对于Python 3.x,用zip
replaceitertools.izip
)。
均田。 你可以检查__length_hint__
方法,但是要注意的是(至less在Python 3.4中,正如gsnedders指出的那样)是一个未logging的实现细节 ( 在线程中的消息之后 ),这可能会消失或召唤鼻魔。
否则,不。 迭代器只是一个只显示next()
方法的对象。 您可以根据需要多次调用它们,最终可能会或可能不会提出StopIteration
。 幸运的是,这种行为在编码器的大部分时间都是透明的。 🙂
一个迭代器就是一个对象,它有一个指向下一个对象的指针,通过某种缓冲区或stream来读取,就像一个LinkedList,你不知道你有多less东西,直到迭代它们。 迭代器意味着高效,因为他们所做的只是通过引用告诉你下一步是什么,而不是使用索引(但是正如你看到你失去了查看下一个条目的能力)。
关于你原来的问题,答案仍然是,通常没有办法知道Python中迭代器的长度。
鉴于你的问题是由pysam库的应用程序驱动的,我可以给出更具体的答案:我是PySAM的贡献者,最终的答案是SAM / BAM文件不提供精确的alignment读取计数。 BAM索引文件也不容易获得这些信息。 最好的方法是在读取多个alignment并根据文件的总大小进行外推之后,通过使用文件指针的位置来估计alignment的大致数量。 这足以实现一个进度条,而不是一个常量计数alignment的方法。
我喜欢这个基数包,它是非常轻量级的,并尝试使用可用的最快实现取决于迭代。
用法:
>>> 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
有两种方法可以在计算机上获得“东西”的长度。
第一种方法是存储一个计数 – 这需要触及文件/数据来修改它的任何东西(或只显示接口的类 – 但归结为相同的东西)。
另一种方法是迭代它,并计算它有多大。
一个快速的基准:
import collections import itertools def count_iter_items(iterable): counter = itertools.count() collections.deque(itertools.izip(iterable, counter), maxlen=0) return next(counter) def count_lencheck(iterable): if hasattr(iterable, '__len__'): return len(iterable) d = collections.deque(enumerate(iterable, 1), maxlen=1) return d[0][0] if d else 0 def count_sum(iter): return sum(1 for _ in iter) iter = (x for x in xrange(100)) %timeit count_iter_items(iter) %timeit count_lencheck(iter) %timeit sum(iter)
结果:
1000000 loops, best of 3: 553 ns per loop 1000000 loops, best of 3: 730 ns per loop 1000000 loops, best of 3: 246 ns per loop
即简单count_sum是要走的路。
将这种types的信息放在文件头中是很常见的做法,而pysam则允许你访问这些信息。 我不知道格式,但你有检查API?
正如其他人所说,你无法知道迭代器的长度。
这是针对迭代器的定义,它是一个指向对象的指针,加上关于如何到达下一个对象的信息。
迭代器不知道能够迭代多less次直到终止。 这可能是无限的,所以无限可能是你的答案。
def count_iter(iter): sum = 0 for _ in iter: sum += 1 return sum
尽pipe通常不可能完成所要求的操作,但迭代完成之后迭代多less个项目仍然非常有用。 为此,您可以使用jaraco.itertools.Counter或类似的。 以下是使用Python 3和rwt加载包的示例。
$ rwt -q jaraco.itertools -- -q >>> import jaraco.itertools >>> items = jaraco.itertools.Counter(range(100)) >>> _ = list(counted) >>> items.count 100 >>> import random >>> def gen(n): ... for i in range(n): ... if random.randint(0, 1) == 0: ... yield i ... >>> items = jaraco.itertools.Counter(gen(100)) >>> _ = list(counted) >>> items.count 48