如果我希望迭代中的项目数量而不关心元素本身,那么实现该目标的Python方法是什么?现在,我将定义
def ilen(it):
return sum(itertools.imap(lambda _: 1, it)) # or just map in Python 3
但我知道自己lambda
几乎被认为是有害的,而且lambda _: 1
肯定不是很漂亮。
(这种情况的用例是计算文本文件中与regex相匹配的行数,即grep -c
。)
如果我希望迭代中的项目数量而不关心元素本身,那么实现该目标的Python方法是什么?现在,我将定义
def ilen(it):
return sum(itertools.imap(lambda _: 1, it)) # or just map in Python 3
但我知道自己lambda
几乎被认为是有害的,而且lambda _: 1
肯定不是很漂亮。
(这种情况的用例是计算文本文件中与regex相匹配的行数,即grep -c
。)
_
一直在使用未使用的变量(Prolog和Haskell编程的习惯)。(1)首先是要提出这个问题的原因。我没有考虑(2)和(3),谢谢指出!
python 3.x
,如果存在重复的项目,并且您还想检查每个项目的计数,请使用Counter(generator/iterator)
,例如c = Counter(iter('goodbadugly'))
,然后对总数进行计数:sum(c.values())
Answers:
itertools.imap()
在Python 2或map()
Python 3中的调用可以用等效的生成器表达式替换:
sum(1 for dummy in it)
这也使用了惰性生成器,因此避免了实现内存中所有迭代器元素的完整列表。
len(list(it))
-或如果元素是唯一的,则len(set(it))
可以保存字符。
len(list(it))
在大多数情况下使用很好。但是,当您有一个懒惰的迭代器产生大量元素时,就不想同时将它们全部存储在内存中只是为了对它们进行计数,这可以避免使用此答案中的代码。
该方法比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代码环路(deque
,count
和zip
在C中的所有实现的); 避免每个循环执行字节码通常是CPython性能的关键。
很难拿出公平的测试用例来比较性能(用list
作弊手段__length_hint__
不可能对任意输入可迭代对象可用,作弊的itertools
功能__length_hint__
通常没有特殊的操作模式,当每次循环返回值时,它们的工作速度更快,这是令人惊讶的。被释放/释放请求先下一值,这deque
与maxlen=0
将做)。我使用的测试用例是使用Python 3.3创建一个生成器函数,该函数将接受输入并返回缺少特殊itertools
返回容器优化或的C级生成器:__length_hint__
yield from
def no_opt_iter(it):
yield from it
然后使用ipython
%timeit
魔术(将不同的常数替换为100):
>>> %%timeit -r5 fakeinput = (0,) * 100
... ilen(no_opt_iter(fakeinput))
当输入的大小不足以len(list(it))
导致内存问题时,在运行Python 3.5 x64的Linux机器上def ilen(it): return len(list(it))
,无论输入长度如何,我的解决方案都比花费大约50%的时间。
为最小的输入,设置成本到呼叫deque
/ zip
/ count
/next
装置需要无限长这种方式比def ilen(it): sum(1 for x in it)
(约200纳秒更我的机器上用于长度为0的输入,这是通过简单增加了33%sum
的方法),但对于输入越长,每个附加元素的运行时间减少一半左右;对于长度为5的输入,成本是等效的,并且在长度50-100之间的某个位置,与实际工作相比,初始开销并不明显;该sum
方法大约需要两倍的时间。
基本上,如果内存使用问题或输入没有限制的大小,并且您更关注速度而不是简洁性,请使用此解决方案。如果输入是有界且很小的,len(list(it))
则可能是最好的;如果输入是无界的,则简单/简洁起着重要作用,则可以使用sum(1 for x in it)
。
more_itertools.ilen
。
maxlen
关键字传递的,而不是位置传递的),但这是固定的开销,在big-O运行时没有意义。无论哪种方式,他们都抄袭了我(我是在3.5年前发布的),而不是相反。:-)
sum(1 ..)
,len(list())
,等。 )到一个人的特殊情况。
一个简短的方法是:
def ilen(it):
return len(list(it))
请注意,如果生成大量元素(例如成千上万个或更多),那么将它们放在列表中可能会成为性能问题。但是,这只是这种想法的简单表达,在大多数情况下,性能并不重要。
sum(1 for i in it)
,只要所有内容都适合内存,它的速度也会比它快。
len(it)
不起作用。sum(it)
,max(it)
,min(it)
等如预期的工作,只是len(it)
没有。
it
是迭代器时,不能保证它知道自己的长度而不会耗尽它。最明显的例子是文件对象。它们的长度取决于文件中的行数,但是行的长度是可变的,要知道行数是多少,唯一的方法是读取整个文件并计算换行数。len()
旨在廉价O(1)
运行;您是否希望它在询问多GB文件的长度时以静默方式读取它们?sum
,max
并且min
是必须读取其数据汇总功能,len
是不是。
count(it)
。
我喜欢基数软件包,它非常轻巧,并根据可迭代性尝试使用最快的实现。
用法:
>>> 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
这些可能是我的选择之一:
print(len([*gen]))
print(len(list(gen)))
list
。除非您可以解释为什么第一个选择有任何优点,否则此答案的含义并不比其他答案有价值。
len([*gen])
很短。例如,这在Code Golf中将很有价值。但是,我同意您的观点,在大多数使用情况下,这种解决方案都不理想。
len([*gen])
对我感到不可思议。
_
用作变量名,因为(1)它容易使人们感到困惑,使他们认为这是一种特殊的语法,(2)_
在交互式解释器中发生冲突,以及(3)与常见的gettext别名发生冲突。