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

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

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)

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

Ответ 1

for item in itertools.islice(iterable, n): - самый очевидный, простой способ сделать это. Он работает для произвольных итераций и является O (n), как и любое разумное решение.

Можно предположить, что другое решение может иметь лучшую производительность; мы не знали бы без времени. Я бы не рекомендовал беспокоиться о сроках, если вы не profile ваш код и найдете этот вызов как горячую точку. Если он не утонет во внутреннем цикле, очень сомнительно, что это произойдет. Преждевременная оптимизация - корень всего зла.


Если бы я искал альтернативные решения, я бы посмотрел на такие, как for count, item in enumerate(iterable): if count > n: break ... и for i in xrange(n): item = next(iterator) .... Я бы не догадался, что это поможет, но, похоже, их стоит попробовать, если мы действительно хотим сравнивать вещи. Если бы я застрял в ситуации, когда я профилировал и обнаружил, что это была горячая точка во внутреннем цикле (это действительно ваша ситуация?), Я также попытался бы облегчить поиск имени из получения атрибута islice глобального iterools привязать функцию к локальному имени.

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

Ответ 2

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 и т.д. - здесь, чтобы отличить микросекунду с половиной разницы, сто тысяч петель примерно правы.

Ответ 3

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

list[:n]

Ответ 4

Вы можете использовать enumerate для записи по существу того же цикла, который у вас есть, но более простым, путинским способом:

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

Ответ 5

Из списка? Попробуйте

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

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

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