如何知道生成的序列最多只有一定的长度

Pet*_*ood 8 python generator

我想知道生成的序列是否少于2个条目.

>>> def sequence():
...     for i in xrange(secret):
...         yield i
Run Code Online (Sandbox Code Playgroud)

我的低效方法是创建一个列表,并测量其长度:

>>> secret = 5
>>> len(list(sequence())) < 2
True
Run Code Online (Sandbox Code Playgroud)

显然,这会消耗整个发电机.

在我的实际情况中,生成器可以遍历大型网络.我想在不消耗整个发电机或构建大型列表的情况下进行检查.

itertools文档中有一个配方:

def take(n, iterable):
    "Return first n items of the iterable as a list"
    return list(islice(iterable, n))
Run Code Online (Sandbox Code Playgroud)

这只会构建一个最大长度列表n,这是更好的.

所以我可以说:

>>> len(take(2, sequence()) < 2
Run Code Online (Sandbox Code Playgroud)

是否有更多的pythonic,有效的方法来做到这一点?

Mar*_*ers 7

从Python 3.4开始,生成器可以实现长度提示.如果生成器实现了它,它将通过该object.__length_hint__()方法暴露.

您可以使用该operator.length_hint()功能进行测试.

如果它不可用,您唯一的选择是消耗元素,您使用take()配方是最有效的方法:

from operator import length_hint
from itertools import chain

elements = []
length = length_hint(gen, None)
if length is None:
    elements = list(take(2, gen))
    length = len(elements)
if length >= 2:
    # raise an error
# use elements, then gen
gen = chain(elements, gen)
Run Code Online (Sandbox Code Playgroud)

  • 根据文档,`__length_hint__`不提供任何保证.它只是一个提示,所以更安全的路线可能*仍然*总是消耗`n`元素(实际上,在许多情况下,正确性约束将*需要*安全路线). (3认同)

Pet*_*ood 1

take使用using的解决方案islice构建一个列表并获取其长度:

>>> from itertools import islice
>>> len(list(islice(sequence(), 2))
2
Run Code Online (Sandbox Code Playgroud)

为了避免创建列表,我们可以使用sum

>>> sum(1 for _ in islice(sequence(), 2)
2
Run Code Online (Sandbox Code Playgroud)

这大约需要 70% 的时间:

>>> timeit('len(list(islice(xrange(1000), 2)))', 'from itertools import islice')
 1.089650974650752

>>> timeit('sum(1 for _ in islice(xrange(1000), 2))', 'from itertools import islice')
0.7579448552500647
Run Code Online (Sandbox Code Playgroud)

总结一下:

>>> def at_most(n, elements):
...     return sum(1 for _ in islice(elements, n + 1)) <= n

>>> at_most(5, xrange(5))
True

>>> at_most(2, xrange(5))
False
Run Code Online (Sandbox Code Playgroud)