Python提供了一种很好的方法来获取渴望迭代的长度,len(x)
也就是说.但是我找不到任何类似于生成器理解和函数所代表的惰性迭代.当然,写下这样的东西并不难:
def iterlen(x): n = 0 try: while True: next(x) n += 1 except StopIteration: pass return n
但我无法摆脱我重新实现自行车的感觉.
(当我输入这个函数时,一个想法让我大吃一惊:也许真的没有这样的功能,因为它"摧毁"了它的论点.虽然不是我的情况的问题).
PS:关于第一个答案 - 是的,类似的东西len(list(x))
也会起作用,但这会大大增加内存的使用.
PPS:重新检查......无视PS,似乎我在尝试时犯了一个错误,它运行正常.抱歉,添麻烦了.
最简单的方法可能就是sum(1 for _ in gen)
gen是你的发电机.
没有一个,因为你不能在一般情况下这样做 - 如果你有一个懒惰的无限发电机怎么办?例如:
def fib(): a, b = 0, 1 while True: a, b = b, a + b yield a
这永远不会终止,但会产生斐波纳契数.您可以通过致电获得尽可能多的Fibonacci数字next()
.
如果你真的需要知道有多少项,那么你无论如何都不能线性地迭代它们,所以只需使用不同的数据结构,例如常规列表.
def count(iter): return sum(1 for _ in iter)
或者更好的是:
def count(iter): try: return len(iter) except TypeError: return sum(1 for _ in iter)
如果它不可迭代,它会抛出一个TypeError
.
或者,如果您想计算生成器中特定的内容:
def count(iter, key=None): if key: if callable(key): return sum(bool(key(x)) for x in iter) return sum(x == key for x in iter) try: return len(iter) except TypeError: return sum(1 for _ in iter)
因此,对于那些想了解该讨论摘要的人。使用以下方法计算长度为5000万的生成器表达式的最终最高分:
len(list(gen))
,
len([_ for _ in gen])
,
sum(1 for _ in gen),
ilen(gen)
(来自more_itertool),
reduce(lambda c, i: c + 1, gen, 0)
,
按执行性能(包括内存消耗)排序,会让您感到惊讶:
```
1:test_list.py:8:0.492 KiBgen = (i for i in data*1000); t0 = monotonic(); len(list(gen))
(“列表,秒”,1.9684218849870376)
2:test_list_compr.py:8:0.867 KiBgen = (i for i in data*1000); t0 = monotonic(); len([i for i in gen])
('list_compr,sec',2.5885991149989422)
3:test_sum.py:8:0.859 KiBgen = (i for i in data*1000); t0 = monotonic(); sum(1 for i in gen); t1 = monotonic()
('sum,sec',3.441088170016883)
4:more_itertools / more.py:413:1.266 KiBd = deque(enumerate(iterable, 1), maxlen=1) test_ilen.py:10: 0.875 KiB gen = (i for i in data*1000); t0 = monotonic(); ilen(gen)
('ilen,sec',9.812256851990242)
5:test_reduce.py:8:0.859 KiBgen = (i for i in data*1000); t0 = monotonic(); reduce(lambda counter, i: counter + 1, gen, 0)
('reduce,sec',13.436614598002052)```
因此,len(list(gen))
是最频繁且消耗较少的内存
您可以使用enumerate()循环生成的数据流,然后返回最后一个数字 - 项目数.
我尝试使用itertools.count()和itertools.izip(),但没有运气.这是我提出的最好/最短的答案:
#!/usr/bin/python import itertools def func(): for i in 'yummy beer': yield i def icount(ifunc): size = -1 # for the case of an empty iterator for size, _ in enumerate(ifunc()): pass return size + 1 print list(func()) print 'icount', icount(func) # ['y', 'u', 'm', 'm', 'y', ' ', 'b', 'e', 'e', 'r'] # icount 10
Kamil Kisiel的解决方案更好:
def count_iterable(i): return sum(1 for e in i)
使用reduce(function,iterable [,initializer])来实现内存高效的纯功能解决方案:
>>> iter = "This string has 30 characters." >>> reduce(lambda acc, e: acc + 1, iter, 0) 30