发电机输出长度

117 python iterable generator

Python提供了一种很好的方法来获取渴望迭代的长度,len(x)也就是说.但是我找不到任何类似于生成器理解和函数所代表的惰性迭代.当然,写下这样的东西并不难:

def iterlen(x):
  n = 0
  try:
    while True:
      next(x)
      n += 1
  except StopIteration: pass
  return n
Run Code Online (Sandbox Code Playgroud)

但我无法摆脱我重新实现自行车的感觉.

(当我输入这个函数时,一个想法让我大吃一惊:也许真的没有这样的功能,因为它"摧毁"了它的论点.虽然不是我的情况的问题).

PS:关于第一个答案 - 是的,类似的东西len(list(x))也会起作用,但这会大大增加内存的使用.

PPS:重新检查......无视PS,似乎我在尝试时犯了一个错误,它运行正常.抱歉,添麻烦了.

小智 225

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

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

Ada*_*eld 33

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

def fib():
    a, b = 0, 1
    while True:
        a, b = b, a + b
        yield a
Run Code Online (Sandbox Code Playgroud)

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

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

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

mpe*_*pen 17

def count(iter):
    return sum(1 for _ in iter)
Run Code Online (Sandbox Code Playgroud)

或者更好的是:

def count(iter):
    try:
        return len(iter)
    except TypeError:
        return sum(1 for _ in iter)
Run Code Online (Sandbox Code Playgroud)

如果它不可迭代,它会抛出一个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)
Run Code Online (Sandbox Code Playgroud)


Ale*_*nov 10

因此,对于那些想了解该讨论摘要的人。使用以下方法计算长度为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))
Run Code Online (Sandbox Code Playgroud)

(“列表,秒”,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])
Run Code Online (Sandbox Code Playgroud)

('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()
Run Code Online (Sandbox Code Playgroud)

('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)
Run Code Online (Sandbox Code Playgroud)

('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)
Run Code Online (Sandbox Code Playgroud)

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

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

  • 我个人发现 len list 方法花费的时间是 sum 方法的两倍。因此结果可能会有所不同。 (3认同)

小智 7

您可以使用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
Run Code Online (Sandbox Code Playgroud)

Kamil Kisiel的解决方案更好:

def count_iterable(i):
    return sum(1 for e in i)
Run Code Online (Sandbox Code Playgroud)


sle*_*anc 6

根据定义,只有一部分生成器会在一定数量的参数(具有预定义的长度)后返回,即便如此,这些有限生成器中也只有一部分具有可预测的结束(访问生成器可能会产生副作用)可以提前停止发电机)。

如果你想为你的生成器实现长度方法,你必须首先定义你认为的“长度”(它是元素的总数?剩余元素的数量?),然后将你的生成器包装在一个类中。下面是一个例子:

class MyFib(object):
    """
    A class iterator that iterates through values of the
    Fibonacci sequence, until, optionally, a maximum length is reached.
    """

    def __init__(self, length):
        self._length = length
        self._i = 0

     def __iter__(self):
        a, b = 0, 1
        while not self._length or self._i < self._length:
            a, b = b, a + b
            self._i += 1
            yield a

    def __len__(self):
        "This method returns the total number of elements"
        if self._length:
            return self._length
        else:
            raise NotImplementedError("Infinite sequence has no length")
            # or simply return None / 0 depending
            # on implementation
Run Code Online (Sandbox Code Playgroud)

以下是如何使用它:

In [151]: mf = MyFib(20)

In [152]: len(mf)
Out[152]: 20

In [153]: l = [n for n in mf]

In [154]: len(l)
Out[154]: 20

In [155]: l
Out[155]: 
[1,
 1,
 2,
...
6765]


In [156]: mf0 = MyFib(0)

In [157]: len(mf0)
---------------------------------------------------------------------------
NotImplementedError                       Traceback (most recent call last)
<ipython-input-157-2e89b32ad3e4> in <module>()
----> 1 len(mf0)

/tmp/ipython_edit_TWcV1I.py in __len__(self)
     22             return self._length
     23         else:
---> 24             raise NotImplementedError
     25             # or simply return None / 0 depending
     26             # on implementation

NotImplementedError: 

In [158]: g = iter(mf0)

In [159]: l0 = [g.next(), g.next(), g.next()]

In [160]: l0
Out[160]: [1, 1, 2]
Run Code Online (Sandbox Code Playgroud)


Oli*_*ain 5

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

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


pyl*_*ang 5

试试这个more_itertools包以获得一个简单的解决方案。例子:

>>> import more_itertools

>>> it = iter("abcde")                                         # sample generator
>>> it
<str_iterator at 0x4ab3630>

>>> more_itertools.ilen(it)
5
Run Code Online (Sandbox Code Playgroud)

有关另一个应用示例,请参阅此帖子


归档时间:

查看次数:

58007 次

最近记录:

6 年,11 月 前