Я пытался найти самый быстрый способ считать количество объектов в списке, соответствующем определенному фильтру. В этом случае, находя, сколько нечетных чисел там находится в списке.
При выполнении этого я был удивлен результатами сравнения понимания списка по сравнению с эквивалентным выражением генератора:
python -m timeit -s "L = xrange(1000000)" "sum([1 for i in L if i & 1])"
10 loops, best of 3: 109 msec per loop
python -m timeit -s "L = xrange(1000000)" "sum(1 for i in L if i & 1)"
10 loops, best of 3: 125 msec per loop
Я также попробовал L быть обычным списком и различными размерами, но во всех случаях победы понимания списка.
Что genexp делает, который заставляет его быть медленнее по сравнению с listcomp, который создает новый список с 1 миллионом объектов...?
(Btw, самый быстрый способ, которым я нашел, был: x = 1; len(filter(x.__and__, L))
. И да, я знаю, что написание кода как этот уничтожает котят, я делаю его ради удовольствия),
Когда доступна по существу неограниченная память (что неизменно будет происходить в крошечных бенчмарках, хотя часто и не в реальных проблемах!-), списки будут иметь тенденцию опережать генераторы, потому что они могут быть распределены только один раз, в одной "большой связке" (без фрагментации памяти и т.д.), в то время как генераторы требуют (внутренне) дополнительных усилий, чтобы избежать этого "большого связки" путем сохранения состояния стек-фрейма, чтобы позволить возобновить исполнение.
Будет ли законченный подход или генератор-подход быстрее в реальной программе зависит от точной ситуации с памятью, в том числе от фрагментации, которую почти невозможно точно воспроизвести в "микро-бенчмарке". IOW, в конце концов, если вы действительно заботитесь о производительности, вы должны тщательно сравнивать (и, отдельно, профиль) вашу реальную программу (программы), а не только "игрушечные" микробенчмарки, в общем случае.
Насколько я помню, кадр генератора должен быть активирован для каждого результата, тогда как понимание списка использует один кадр активации. Дополнительные затраты при сжатии списка - это добавленная стоимость памяти - в вашем случае ссылки на int. Отношение может полностью измениться, если каждый элемент является новым экземпляром и использует больше памяти. Обновление
: после тестирования оно полностью изменило
~% python -m timeit -s "L = xrange(1000000);oint=type('intEx', (int,),{})" "sum([oint(1) for i in L if i & 1])"
10 loops, best of 3: 414 msec per loop
~% python -m timeit -s "L = xrange(1000000);oint=type('intEx', (int,),{})" "sum(oint(1) for i in L if i & 1)"
10 loops, best of 3: 392 msec per loop