如何len(发生器())
Python生成器是非常有用的。 它们比返回列表的函数有优势。 但是,你可以len(list_returning_function())
。 有没有办法len(generator_function())
?
更新:
当然len(list(generator_function()))
会工作…..
我正在尝试使用我在创build的新生成器中创build的生成器。 作为新生成器计算的一部分,它需要知道旧生成器的长度。 不过,我想保持它们与发生器的属性相同,特别是不要将整个列表保存在内存中,因为它可能会很长。
更新2:
假设从第一步开始generotr 知道它的目标长度。 另外,没有理由维护len()
语法。 示例 – 如果Python中的函数是对象,我不能将该长度分配给该对象的一个variables,这个variables可以被新的生成器访问吗?
发电机没有长度,他们毕竟不是收集的。
生成器是具有内部状态的函数 (以及花哨的语法)。 你可以重复地调用它们来得到一系列的值,所以你可以在循环中使用它们。 但是它们不包含任何元素,所以要求生成器的长度就像是要求函数的长度一样。
如果Python中的函数是对象,我不能将这个长度赋给这个对象的一个variables,这个variables可以被新的生成器访问吗?
函数是对象,但不能为其分配新的属性。 原因可能是尽可能保持这样一个基本的对象。
但是,您可以简单地从函数中返回(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)
在其他答案中已经提出的转换list
是最好的方法,如果你仍然想处理发生器元素,但有一个缺陷:它使用O(n)内存。 你可以计算一个发生器中的元素,而不使用那么多的内存:
sum(1 for x in generator)
当然,请注意,在普通的Python实现中,这可能比len(list(generator))
要慢,而且如果这些生成器足够长以避免内存复杂度的问题,那么操作将需要相当长的一段时间。 尽pipe如此,我个人更喜欢这个解决scheme,因为它描述了我想要得到的东西,它不会给我任何额外的东西(比如所有元素的列表)。
另外听delnan的build议:如果你丢弃了生成器的输出,很有可能有一种方法来计算元素的数量而不运行它,或者以另一种方式对它们进行计数。
假设我们有一个发生器:
def gen(): for i in range(10): yield i
我们可以将发生器和已知的长度一起包装在一个对象中:
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)
LenGen
实例LenGen
就是生成器,因为调用它们会返回一个迭代器。
现在我们可以使用lgen
生成器来代替gen
,并且还可以访问len(lgen)
:
def new_gen(): for i in lgen(): yield float(i)/len(lgen) for i in new_gen(): print(i)
你可以使用len(list(generator_function())
。但是,这消耗了生成器,但这是唯一的方法,你可以找出有多less元素生成。所以你可能想保存列表的地方,如果你还想要使用项目。
a = list(generator_function()) print(len(a)) print(a[0])
你可以len(list(generator))
但是如果你真的打算放弃结果的话,你可能会更有效率。
你可以使用reduce
。
对于Python 3:
>>> import functools >>> def gen(): ... yield 1 ... yield 2 ... yield 3 ... >>> functools.reduce(lambda x,y: x + 1, gen(), 0)
在Python 2中, reduce
在全局名称空间中,所以导入是不必要的。
您可以通过创build自己的可迭代对象来将发生器的好处与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,
这基本上是xrange
一个简单实现,它返回一个可以带len的对象,但是不会创build一个明确的列表。
您可以使用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