Быстрая итерация по первым N элементам итерационного (не списка) в python

Я ищу питонический способ перебора первого n элементы iterable (upd: не список в обычном случае, так как для списков вещи тривиальны), и это очень важно сделать как можно быстрее. Вот как я это делаю сейчас:--7-->

count = 0
for item in iterable:
 do_something(item)
 count += 1
 if count >= n: break

не кажется мне аккуратным. Другой способ сделать это:

for item in itertools.islice(iterable, n):
    do_something(item)

это выглядит хорошо, вопрос достаточно быстро использовать с некоторыми генераторами? Например:

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

будет это достаточно быстро по сравнению с первым методом? есть более простой способ сделать это?

5 ответов


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 для привязки функции к локальному имени уже.

Это вещи, которые вы делаете только после того, как доказали, что они помогут. Люди часто пытаются делать это в другое время. Это не помогает сделать их программы заметно быстрее; это просто делает их программы хуже.


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)

и видим...:

$ 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

так ясно, itertools здесь быстрее-бенчмарк с вашими собственными данными для проверки.

кстати, я нашел timeit гораздо полезнее из командной строки, так что я всегда использую его - он затем запускает правильный "порядок величины" циклов для вида скорости, которые вы специально пытаетесь измерить, будь то 10, 100, 1000 и так далее-здесь, чтобы различить микросекунду с половиной разницы, сто тысяч петель примерно верны.


Если это список, то вы можете использовать нарезки:

list[:n]

можно использовать перечислить в основном пишут такие же петли, но и в более простые, подходящие для Python образом:

for idx, val in enumerate(iterableobj):
    if idx > n:
        break
    do_something(val)

списка? Попробуй!--3-->

for k in mylist[0:n]:
     # do stuff with k

вы также можете использовать понимание, если вам нужно

my_new_list = [blah(k) for k in mylist[0:n]]