我想知道生成的序列是否少于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,有效的方法来做到这一点?
从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)
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)