如果我希望迭代中的项目数量而不关心元素本身,那么实现该目标的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别名发生冲突。