快速迭代python中可迭代(不是列表)的前n项

mar*_*ext 12 python performance iterator generator

我正在寻找一种迭代迭代迭代的第一n项的pythonic方法(upd:在常见情况下不是列表,因为列表事情是微不足道的),并且尽可能快地执行此操作非常重要.这是我现在这样做的方式:

count = 0
for item in iterable:
 do_something(item)
 count += 1
 if count >= n: break
Run Code Online (Sandbox Code Playgroud)

对我来说似乎并不整洁.另一种方法是:

for item in itertools.islice(iterable, n):
    do_something(item)
Run Code Online (Sandbox Code Playgroud)

这看起来不错,问题是它是否足够快与一些发电机一起使用?例如:

pair_generator = lambda iterable: itertools.izip(*[iter(iterable)]*2)
for item in itertools.islice(pair_generator(iterable), n):
 so_something(item)
Run Code Online (Sandbox Code Playgroud)

与第一种方法相比,它运行得足够快吗?有没有更简单的方法呢?

Mik*_*ham 15

for item in itertools.islice(iterable, n):这是最明显,最简单的方法.它适用于任意迭代,并且是O(n),就像任何理智的解决方案一样.

可以想象,另一种解决方案可以有更好的性能; 没有时间我们就不会知道.我不建议打扰时间,除非你描述你的代码并发现这个电话是一个热点.除非它在内环中被掩埋,否则它将是非常值得怀疑的.过早优化是万恶之源.


如果我要去寻找替代解决方案,我会看的像for count, item in enumerate(iterable): if count > n: break ...for i in xrange(n): item = next(iterator) ....我不认为这会有所帮助,但如果我们真的想比较一下,它们似乎值得尝试.如果我被困在我描述的情况下发现这是一个内循环中的热点(这真的是你的情况吗?),我也会尝试简化名称查找,从获取islice全局属性iterools到绑定函数到一个本地名称.

这些是你在证明他们会帮助之后才做的事情.人们会尝试其他时间做很多事情.它并没有帮助使他们的程序明显更快; 它只会使他们的程序变得更糟.


Ale*_*lli 6

itertools 在直接适用时,它往往是最快的解决方案.

显然,检查的唯一方法是基准 - 例如,保存 aaa.py

import itertools

def doit1(iterable, n, do_something=lambda x: None):
  count = 0
  for item in iterable:
   do_something(item)
   count += 1
   if count >= n: break

def doit2(iterable, n, do_something=lambda x: None):
  for item in itertools.islice(iterable, n):
      do_something(item)

pair_generator = lambda iterable: itertools.izip(*[iter(iterable)]*2)

def dd1(itrbl=range(44)): doit1(itrbl, 23)
def dd2(itrbl=range(44)): doit2(itrbl, 23)
Run Code Online (Sandbox Code Playgroud)

并看到......:

$ python -mtimeit -s'import aaa' 'aaa.dd1()'
100000 loops, best of 3: 8.82 usec per loop
$ python -mtimeit -s'import aaa' 'aaa.dd2()'
100000 loops, best of 3: 6.33 usec per loop
Run Code Online (Sandbox Code Playgroud)

很明显,itertools在这里更快 - 用你自己的数据进行基准测试来验证.

顺便说一句,我发现timeit从命令行中可以使用的更多,所以我总是使用它 - 它然后为你特别想要测量的那种速度运行正确的"数量级"循环,那些是10, 100,1000等等 - 在这里,为了区分一微秒和一半的差异,十万个循环是正确的.