2013-06-03 2 views
3

Почему список постижений имеют лучшую производительность, чем для loop, in Python?Python List Comprehension vs for

список понимание:

new_items = [a for a in items if a > 10] 

цикл:

new_items = [] 
for a in items: 
    if a > 10: new_items.append(a) 

Существуют и другие примеры (не петли), где одна структура Python имеет худшую производительность, чем другая структура Python?

+2

* Есть ли другой пример (не цикл), когда одна структура Python имеет худшую производительность по сравнению с другой структурой Python?* - Их бесконечно много. –

+0

@ Lattyware, например? –

+5

Вы не должны беспокоиться о lc vs loop, если производительность является единственной причиной. Вместо этого будьте обеспокоены тем, что ваш общий алгоритм звучит и беспокоится о том, что вы можете понять код, который вы написали хорошо. – dawg

ответ

8

По существу, понимание списка и циклов делает довольно похожие вещи, с пониманием списка, устраняющими некоторые накладные расходы и заставляющими его выглядеть красиво. Чтобы понять, почему это происходит быстрее, вы должны смотреть в Efficiency of list comprehensions и процитировать соответствующую часть вашей проблемы:

описаний списков работать лучше здесь, потому что вам не нужно загружать атрибут на добавление от списка (программа цикла, байт-код 28) и называют ее функцией (программа цикла, байт-код 38). Вместо этого, в понимании , создается специальный байт-код LIST_APPEND для быстрого добавления в список результатов (программа понимания, байт-код 33).

В программе loop_faster, можно избежать накладных расходов на Append поиска атрибута поднятия его из петли и размещения результата в fastlocal (9-12) байт-кода, так что петли более быстро; однако в определении используется специализированный байт-код LIST_APPEND вместо , несущий накладные расходы на вызов функции, поэтому он все еще козыряется.

Ссылка также содержит информацию о некоторых возможных ловушках, связанных с lc, и я бы рекомендовал вам пройти через нее один раз.

+0

также, если вы хотите, чтобы время вашего кода вы могли использовать ''% timeit' 'magic funciton в ipython – goofd

+0

Также LC не должен беспокоиться о вещах вроде операторов, поскольку в LC разрешены только выражения. –

+0

Это некрасиво. Это очень длинное заявление на первом месте, поэтому я должен разбить его на несколько строк. Как только это так, понимание не имеет для меня никакого смысла: '[[1 if item_idx == row_idx else 0 для item_idx в диапазоне (0, 3)] для row_idx в диапазоне (0, 3)]', что является примером из https://python-3-patterns-idioms-test.readthedocs.org/en/latest/Comprehensions.html. – Schultz9999

1

От the python wiki

для утверждения наиболее часто используемые. Он перебирает элементы последовательности, присваивая каждому переменную цикла. Если тело вашего цикла прост, служебные данные интерпретатора самого цикла for могут быть значительным объемом служебных данных. Здесь удобно использовать функцию карты . Вы можете думать о карте как о для перехода на C-код.

Так просто для циклов есть накладные расходы, которые понимают списки.

3

Предполагая, что мы говорим здесь CPython, вы можете использовать dis модуль для сравнения сгенерированных байткодов:

>> def one(): 
     return [a for a in items if a > 10] 

>> def two(): 
     res = [] 
     for a in items: 
      if a > 10: 
       res.append(a) 

>> dis.dis(one) 

    2   0 BUILD_LIST    0 
       3 LOAD_GLOBAL    0 (items) 
       6 GET_ITER 
     >> 7 FOR_ITER    24 (to 34) 
      10 STORE_FAST    0 (a) 
      13 LOAD_FAST    0 (a) 
      16 LOAD_CONST    1 (10) 
      19 COMPARE_OP    4 (>) 
      22 POP_JUMP_IF_FALSE  7 
      25 LOAD_FAST    0 (a) 
      28 LIST_APPEND    2 
      31 JUMP_ABSOLUTE   7 
     >> 34 RETURN_VALUE 

>> dis.dis(two) 
    2   0 BUILD_LIST    0 
       3 STORE_FAST    0 (res) 

    3   6 SETUP_LOOP    42 (to 51) 
       9 LOAD_GLOBAL    0 (items) 
      12 GET_ITER 
     >> 13 FOR_ITER    34 (to 50) 
      16 STORE_FAST    1 (a) 

    4   19 LOAD_FAST    1 (a) 
      22 LOAD_CONST    1 (10) 
      25 COMPARE_OP    4 (>) 
      28 POP_JUMP_IF_FALSE  13 

    5   31 LOAD_FAST    0 (res) 
      34 LOAD_ATTR    1 (append) 
      37 LOAD_FAST    1 (a) 
      40 CALL_FUNCTION   1 
      43 POP_TOP 
      44 JUMP_ABSOLUTE   13 
      47 JUMP_ABSOLUTE   13 
     >> 50 POP_BLOCK 
     >> 51 LOAD_CONST    0 (None) 
      54 RETURN_VALUE 

Таким образом, для одной вещи, список понимание использует выделенный LIST_APPEND опкода, который не является используя цикл for.