在Python的itertools文档中,提供了以下关于迭代器向前推进n步的“recipe”(配方):
我想知道这个配方为什么与这个有根本性的不同(除了处理整个迭代器的方法):
我使用
注意:即使我不使用从上面文档中显示的python等效定义
编辑:
def consume(iterator, n):
"Advance the iterator n-steps ahead. If n is none, consume entirely."
# Use functions that consume iterators at C speed.
if n is None:
# feed the entire iterator into a zero-length deque
collections.deque(iterator, maxlen=0)
else:
# advance to the empty slice starting at position n
next(islice(iterator, n, n), None)
我想知道这个配方为什么与这个有根本性的不同(除了处理整个迭代器的方法):
def other_consume(iterable, n):
for i in xrange(n):
next(iterable, None)
我使用
timeit
确认了,如预期的那样,上述方法要慢得多。是什么让这种优越的性能在这个配方中发挥作用?我知道它使用了islice
,但看着islice
,它似乎基本上正在做与上面的代码完全相同的事情:def islice(iterable, *args):
s = slice(*args)
it = iter(xrange(s.start or 0, s.stop or sys.maxint, s.step or 1))
nexti = next(it)
### it seems as if this loop yields from the iterable n times via enumerate
### how is this different from calling next n times?
for i, element in enumerate(iterable):
if i == nexti:
yield element
nexti = next(it)
注意:即使我不使用从上面文档中显示的python等效定义
islice
,而是从itertools
导入它,该配方仍然更快。编辑:
timeit
代码在这里:timeit.timeit('a = iter([random() for i in xrange(1000000)]); consume(a, 1000000)', setup="from __main__ import consume,random", number=10)
timeit.timeit('a = iter([random() for i in xrange(1000000)]); other_consume(a, 1000000)', setup="from __main__ import other_consume,random", number=10)
每次运行时,other_consume
的速度大约慢了2.5倍。
islice
函数并不正确。当start
和stop
都相同时它什么也不做(例如next(iter(xrange(100, 100)))
会立刻抛出StopIteration
异常)。而consume
函数调用该函数时正是以这种方式,所以你的consume
函数将不会消耗任何东西(但非常快速地不消耗!)。 - Blckknghtenumerate
直接循环迭代器的纯Python版本大约需要比itertools
配方长7.5倍。你提出的简单代码需要的时间几乎是后者的两倍(在我的测试中,比itertools
代码慢14倍)。因此,我认为@MartijnPieters在他的答案中提到的加倍的next
调用负责了你看到的一半减速。其余的故事是,任何Python循环都会比C中的等效循环慢。 - Blckknght