Быстрая итерация по первым 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]]