如何len(generator())

Jon*_*han 128 python generator

Python生成器非常有用.它们优于返回列表的函数.但是,你可以len(list_returning_function()).有办法len(generator_function())吗?

更新:
当然len(list(generator_function()))会工作.....
我正在尝试使用我在我正在创建的新生成器中创建的生成器.作为新发电机计算的一部分,它需要知道旧发电机的长度.但是我想将它们与发生器保持相同的属性,特别是 - 不要将整个列表保存在内存中,因为它可能长.

更新2:
假设发生器即使从第一步开始就知道它的目标长度.此外,没有理由维护len()语法.示例 - 如果Python中的函数是对象,我不能将长度分配给新生成器可以访问的此对象的变量吗?

Ros*_*ron 218

list如果你仍然想要在之后处理生成器元素,那么在其他答案中建议的转换是最好的方法,但是有一个缺陷:它使用O(n)内存.您可以在不使用那么多内存的情况下计算生成器中的元素:

sum(1 for x in generator)
Run Code Online (Sandbox Code Playgroud)

当然,要注意这可能比len(list(generator))普通的Python实现要慢,如果生成器足够长,内存复杂性很重要,那么操作需要相当长的时间.尽管如此,我个人更喜欢这个解决方案,因为它描述了我想要的东西,而且它没有给我任何额外的不需要的东西(例如所有元素的列表).

还要听听delnan的建议:如果你丢弃了生成器的输出,很可能有一种方法可以在不运行它的情况下计算元素的数量,或者以另一种方式计算它们.

  • 这是最好的答案imo.但是,写入会略微更加pythonic:sum(发生器中的_为1) (32认同)
  • @sschuberth:你是对的.如果您需要长度和值(并且您不控制生成器的原点),将其转换为列表是最佳选择. (4认同)
  • 在 scikit-learn 的源代码中找到了这个 :) https://github.com/scikit-learn/scikit-learn/blob/a24c8b46/sklearn/preprocessing/data.py#L1314 (3认同)
  • 作为一个 Python 菜鸟,我可能遗漏了一些明显的东西,但是如果您之后不能再使用生成器来生成实际值,那么使用这种方法有什么意义,因为您已经使用它来计算值(并且生成器是一次性的AFAIU)? (2认同)
  • @sschuberth 如果我正在编写测试并想断言某些返回的事物的数量,但不关心它们是如何或是什么 (2认同)
  • 为什么你认为这可能比“len(list(generator))”慢? (2认同)

Joc*_*zel 60

生成器没有长度,毕竟它们不是集合.

生成器是具有内部状态(和花哨语法)的函数.您可以重复调用它们以获取一系列值,因此您可以在循环中使用它们.但是它们不包含任何元素,因此要求生成器的长度就像要求函数的长度一样.

如果Python中的函数是对象,我不能将长度分配给新生成器可以访问的此对象的变量吗?

函数是对象,但您无法为它们分配新属性.原因可能是保持这样一个基本对象尽可能高效.

但是,您可以简单地(generator, length)从函数中返回对,或者将生成器包装在一个简单的对象中,如下所示:

class GeneratorLen(object):
    def __init__(self, gen, length):
        self.gen = gen
        self.length = length

    def __len__(self): 
        return self.length

    def __iter__(self):
        return self.gen

g = some_generator()
h = GeneratorLen(g, 1)
print len(h), list(h)
Run Code Online (Sandbox Code Playgroud)

  • 不,很明显,讨论许多发生器的长度是有意义的,因为许多发生器返回有限数量的元素.你认为它毫无意义的论证证明了太多; 如果我们接受它,那么通过相同的逻辑,将生成器的输出转换为列表也没有意义......然而`list(generator())`工作并且内置于语言中. (100认同)
  • @KevinJ.Chase我也可以问*"什么会总和(itertools.count())`返回?"*,但`sum`可以带生成器.有一种显而易见的方法可以在任意迭代上实现`len()`:让它迭代并计算有多少元素.我认为这将是一个无用的功能(知道消耗的生成器的长度,你丢弃的元素在大多数情况下不会有用),但事实上它将永远循环在无限生成器上显然不是你认为它的击倒参数,因为`sum()`和`list()`具有相同的行为. (4认同)
  • ...并且"返回消耗的生成器的长度,其元素已被丢弃"****有时是有用的:作为生成器链的最外层消费者,您只想报告内部生成器操作的元素数量(例如,写了多少数据库记录). (2认同)
  • @Roch Oxymoron的答案正是我所寻找的. (2认同)

unu*_*tbu 19

假设我们有一台发电机:

def gen():
    for i in range(10):
        yield i
Run Code Online (Sandbox Code Playgroud)

我们可以将生成器与已知长度一起包装在一个对象中:

import itertools
class LenGen(object):
    def __init__(self,gen,length):
        self.gen=gen
        self.length=length
    def __call__(self):
        return itertools.islice(self.gen(),self.length)
    def __len__(self):
        return self.length

lgen=LenGen(gen,10)
Run Code Online (Sandbox Code Playgroud)

实例LenGen是生成器本身,因为调用它们会返回迭代器.

现在我们可以使用lgen生成器来代替gen和访问len(lgen):

def new_gen():
    for i in lgen():
        yield float(i)/len(lgen)

for i in new_gen():
    print(i)
Run Code Online (Sandbox Code Playgroud)

  • @Jonathan:我的第一次尝试是将属性附加到生成器对象`gen()`.但是,与函数不同,Python不允许您将其他属性附加到生成器对象.由于这个限制,我去了一堂课. (2认同)

Gre*_*ill 13

你可以用len(list(generator_function()).但是,这会消耗生成器,但这是您可以找出生成多少元素的唯一方法.因此,如果您还想使用这些项目,您可能希望将列表保存在某处.

a = list(generator_function())
print(len(a))
print(a[0])
Run Code Online (Sandbox Code Playgroud)

  • 问题是,除了消耗生成器(这显然是必要的,除非生成器明确提供对 `.__len__()` 的支持)之外,这**还会一次性将生成器的所有项目临时存储在 RAM 中**。 (2认同)

hwi*_*ers 8

您可以使用reduce.

对于 Python 3:

>>> import functools
>>> def gen():
...     yield 1
...     yield 2
...     yield 3
...
>>> functools.reduce(lambda x,y: x + 1, gen(), 0)
Run Code Online (Sandbox Code Playgroud)

在 Python 2 中,reduce位于全局命名空间中,因此不需要导入。


Ben*_*son 7

你可以,len(list(generator))但如果你真的打算丢弃结果,你可能会提高效率.


cyb*_*org 6

你可以send用作黑客:

def counter():
    length = 10
    i = 0
    while i < length:
        val = (yield i)
        if val == 'length':
            yield length
        i += 1

it = counter()
print(it.next())
#0
print(it.next())
#1
print(it.send('length'))
#10
print(it.next())
#2
print(it.next())
#3
Run Code Online (Sandbox Code Playgroud)


Ned*_*der 5

您可以len()通过创建自己的可迭代对象,将生成器的优点与 的确定性结合起来:

class MyIterable(object):
    def __init__(self, n):
        self.n = n

    def __len__(self):
        return self.n

    def __iter__(self):
        self._gen = self._generator()
        return self

    def _generator(self):
        # Put your generator code here
        i = 0
        while i < self.n:
            yield i
            i += 1

    def next(self):
        return next(self._gen)

mi = MyIterable(100)
print len(mi)
for i in mi:
    print i,
Run Code Online (Sandbox Code Playgroud)

这基本上是 的一个简单实现xrange,它返回一个可以获取 len 的对象,但不创建显式列表。


归档时间:

查看次数:

94561 次

最近记录:

6 年,11 月 前