当前位置:  开发笔记 > 编程语言 > 正文

发电机输出长度

如何解决《发电机输出长度》经验,为你挑选了6个好方法。

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,似乎我在尝试时犯了一个错误,它运行正常.抱歉,添麻烦了.



1> 小智..:

最简单的方法可能就是sum(1 for _ in gen)gen是你的发电机.


@CharlesSalvia这是对imho的评论.获得发电机的长度值得评论我会说.
另一个主要的缺点是它只是为了获得长度而耗尽发电机,这通常首先会破坏发电机的整个目的.
尽管我喜欢这个解决方案,但这里的主要缺点是,通过阅读您想要实现的代码并不是很明显.如果我在其他人的代码中看到这一行,我会停下来思考"为什么他在这里拿这笔钱?" - 除非我以前见过这个"黑客".
请注意,这可能会减少内存消耗,但似乎比简单地将其转换为列表要慢.

2> Adam Rosenfi..:

没有一个,因为你不能在一般情况下这样做 - 如果你有一个懒惰的无限发电机怎么办?例如:

def fib():
    a, b = 0, 1
    while True:
        a, b = b, a + b
        yield a

这永远不会终止,但会产生斐波纳契数.您可以通过致电获得尽可能多的Fibonacci数字next().

如果你真的需要知道有多少项,那么你无论如何都不能线性地迭代它们,所以只需使用不同的数据结构,例如常规列表.


我不确定我是否相信/接受这个解释.`sum`采用可迭代,即使可迭代可能是无限的,因此"在一般情况下你不能这样做",在一般情况下你可以做len.也许更可能的理由是人们"期望"`len`是O(1),而不是一般的可迭代?
常规列表消耗更多内存,这是OP想要避免的.

3> mpen..:
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)



4> Alex-Bogdano..:

因此,对于那些想了解该讨论摘要的人。使用以下方法计算长度为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 KiB
gen = (i for i in data*1000); t0 = monotonic(); len(list(gen))

(“列表,秒”,1.9684218849870376)

2:test_list_compr.py:8:0.867 KiB
gen = (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 KiB
gen = (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 KiB
d = 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 KiB
gen = (i for i in data*1000); t0 = monotonic(); reduce(lambda counter, i: counter + 1, gen, 0)

('reduce,sec',13.436614598002052)```

因此,len(list(gen))是最频繁且消耗较少的内存



5> 小智..:

您可以使用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)



6> OlivierBlanv..:

使用reduce(function,iterable [,initializer])来实现内存高效的纯功能解决方案:

>>> iter = "This string has 30 characters."
>>> reduce(lambda acc, e: acc + 1, iter, 0)
30

推荐阅读
吻过彩虹的脸_378
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有