发电机输出的长度

Python提供了一个很好的方法来获得渴望迭代的长度,即len(x) 。 但是我找不到任何类似于由发生器理解和函数表示的惰性迭代。 当然,写这样的东西并不难:

 def iterlen(x): n = 0 try: while True: next(x) n += 1 except StopIteration: pass return n 

但我无法摆脱我正在重新实施自行车的感觉。

(当我input这个函数的时候,我心里想着一个念头:也许实际上没有这样的function,因为它“破坏了”它的论点,但是对我来说不是问题)。

PS:关于第一个答案 – 是的,像len(list(x))这样的东西也可以工作,但这大大增加了内存的使用。

PPS:重新检查…不顾PS,似乎我犯了一个错误,而尝试,它工作正常。 抱歉,添麻烦了。

没有一个是因为在一般情况下你不能这样做 – 如果你有一个懒惰的无限生成器呢? 例如:

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

这从来没有终止,但会产生斐波纳契数字。 您可以通过调用next()来获得任意数量的斐波纳契数字。

如果你真的需要知道有多less个项目,那么无论如何都不能一次一个地迭代它们,所以只需要使用一个不同的数据结构,比如一个常规列表。

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

 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) 

你可以使用enumerate()循环生成的数据stream,然后返回最后一个数字 – 项目的数量。

我试图用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 

卡米尔Kisiel的解决scheme是更好的:

 def count_iterable(i): return sum(1 for e in i) 

使用reduce(函数,iterable [,初始化程序])为一个高效的内存function的解决scheme:

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

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

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

 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 

以下是如何使用它:

 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] 

尝试一个简单的解决schememore_itertools包。 例:

 >>> import more_itertools >>> it = iter("abcde") # sample generator >>> it <str_iterator at 0x4ab3630> >>> more_itertools.ilen(it) 5 

看到这个post的另一个应用的例子。

这是一个黑客攻击,但是如果你真的想在一个普通的迭代器上工作,你可以创build你自己的len版本。

len函数本质上等同于下面的内容(尽pipe实现通常提供一些优化来避免额外的查找):

 def len(iterable): return iterable.__len__() 

因此,我们可以定义我们的new_len来尝试,如果__len__不存在,则通过使用可迭代来自我计数元素的数量:

 def new_len(iterable): try: return iterable.__len__() except AttributeError: return sum(1 for _ in iterable) 

Python 2/3中的上述工作,以及(据我所知)应该涵盖每一种可以想象的迭代。