计算生成器/迭代器中项目数的最短方法是什么?

时间:2011-03-21 22:35:57

标签: python iterator generator iterable

如果我想要迭代中的项目数而不关心元素本身,那么获得该元素的pythonic方法是什么?现在,我要定义

def ilen(it):
    return sum(itertools.imap(lambda _: 1, it))    # or just map in Python 3

但我理解lambda接近被视为有害,lambda _: 1肯定不是很好。

(用例是计算与正则表达式匹配的文本文件中的行数,即grep -c。)

7 个答案:

答案 0 :(得分:124)

通常的方法是

sum(1 for i in it)

答案 1 :(得分:24)

当迭代可能很长时(而且当可迭代性很短时没有意义地慢)时,有效地快于sum(1 for i in it)的方法,同时保持固定的内存开销行为(与len(list(it))不同)以避免交换抖动和更大输入的重新分配开销:

# On Python 2 only, get zip that lazily generates results instead of returning list
from future_builtins import zip

from collections import deque
from itertools import count

def ilen(it):
    # Make a stateful counting iterator
    cnt = count()
    # zip it with the input iterator, then drain until input exhausted at C level
    deque(zip(it, cnt), 0) # cnt must be second zip arg to avoid advancing too far
    # Since count 0 based, the next value is the count
    return next(cnt)

len(list(it))类似,它在CPython上执行C代码循环(dequecountzip都在C)中实现;避免每个循环执行字节代码通常是CPython中性能的关键。

用比较性能(list使用__length_hint__作弊的公平测试用例是非常困难的,这对于任意输入的迭代,itertools函数来说是不可能的__length_hint__提供deque通常具有特殊的操作模式,当在请求下一个值之前释放每个循环上返回的值时,该模式可以更快地工作,maxlen=0 itertools将执行此操作。我使用的测试用例是创建一个生成器函数,该函数将使用Python 3.3 __length_hint__获取输入并返回缺少特殊yield from返回容器优化或def no_opt_iter(it): yield from it 的C级生成器。 :

ipython

然后使用%timeit >>> %%timeit -r5 fakeinput = (0,) * 100 ... ilen(no_opt_iter(fakeinput)) magic(用不同的常量替换100):

len(list(it))

当输入不够大而def ilen(it): return len(list(it))会导致内存问题时,在运行Python 3.5 x64的Linux机器上,无论输入长度如何,我的解决方案都比deque长约50%。

对于最小的输入,调用zip / count / next / def ilen(it): sum(1 for x in it)的设置费用意味着它的使用时间比sum慢很多(对于长度为0的输入,我的机器上大约200 ns,比简单的sum方法增加了33%),但是对于更长的输入,它在每个附加元素的大约一半时间内运行;对于长度为5的输入,成本是等价的,并且在50-100范围内的某处,与实际工作相比,初始开销是不明显的; len(list(it))方法大约需要两倍的时间。

基本上,如果内存使用很重要或输入没有有限的大小而你关心速度而不是简洁,请使用此解决方案。如果输入有界且很小,sum(1 for x in it)可能是最好的,如果它们无限制,但简单/简洁很重要,则使用 $request = Illuminate\Http\Request::capture(); $routes = [ 'route', 'route2', 'route3', 'route4', ]; if (in_array($request->url(), $routes) { // do whatever you are doing to route to your legacy project } else { $response = $kernel->handle( $request = Illuminate\Http\Request::capture() ); $response->send(); $kernel->terminate($request, $response); }

答案 2 :(得分:7)

简短的方法是:

def ilen(it):
    return len(list(it))

请注意,如果您要生成 lot 元素(例如,数万或更多),那么将它们放入列表可能会成为性能问题。然而,这是一个简单的表达方式,在大多数情况下性能不重要。

答案 3 :(得分:3)

more_itertools是实施ilen工具的第三方库。 pip install more_itertools

import more_itertools as mit


mit.ilen(x for x in range(10))
# 10

答案 4 :(得分:2)

len(list(it))

不过,如果是无限生成器,它可能会挂掉。

答案 5 :(得分:1)

我喜欢这个cardinality包,它非常轻量级,并尝试使用可用的最快实现,具体取决于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

答案 6 :(得分:0)

这些是我的选择之一:

print(len([*gen]))
print(len(list(gen)))