tutorial for español else comprehension python list python-2.7 python-3.x list-comprehension

python - for - ¿Por qué la comprensión de una lista es mucho más rápida que agregarla a una lista?



python list comprehension if (3)

Citando this artículo, se debe a que el atributo append de la list no se busca, carga y llama como una función, lo que lleva tiempo y se suma a las iteraciones.

Me preguntaba por qué la comprensión de la lista es mucho más rápida que agregarla a una lista. Pensé que la diferencia es simplemente expresiva, pero no lo es.

>>> import timeit >>> timeit.timeit(stmt=''''''/ t = [] for i in range(10000): t.append(i)'''''', number=10000) 9.467898777974142 >>> timeit.timeit(stmt=''t= [i for i in range(10000)]'', number=10000) 4.1138417314859

La comprensión de la lista es un 50% más rápida. ¿Por qué?


Incluso teniendo en cuenta el tiempo que lleva buscar y cargar la función de append , la comprensión de la lista es aún más rápida porque la lista se crea en C, en lugar de crear un elemento a la vez en Python.

# Slow timeit.timeit(stmt='''''' for i in range(10000): t.append(i)'''''', setup=''t=[]'', number=10000) # Faster timeit.timeit(stmt='''''' for i in range(10000): l(i)'''''', setup=''t=[]; l=t.append'', number=10000) # Faster still timeit.timeit(stmt=''t = [i for i in range(10000)]'', number=10000)


La comprensión de la lista es básicamente un "azúcar sintáctico" para el ciclo for regular. En este caso, la razón por la que funciona mejor es porque no necesita cargar el atributo append de la lista y llamarlo como una función en cada iteración. En otras palabras y en general , las comprensiones de listas funcionan más rápido porque suspender y reanudar el marco de una función, o múltiples funciones en otros casos, es más lento que crear una lista a pedido.

Considere los siguientes ejemplos:

# Python-3.6 In [1]: import dis In [2]: def f1(): ...: l = [] ...: for i in range(5): ...: l.append(i) ...: In [3]: def f2(): ...: [i for i in range(5)] ...: In [4]: dis.dis(f1) 2 0 BUILD_LIST 0 3 STORE_FAST 0 (l) 3 6 SETUP_LOOP 33 (to 42) 9 LOAD_GLOBAL 0 (range) 12 LOAD_CONST 1 (5) 15 CALL_FUNCTION 1 (1 positional, 0 keyword pair) 18 GET_ITER >> 19 FOR_ITER 19 (to 41) 22 STORE_FAST 1 (i) 4 25 LOAD_FAST 0 (l) 28 LOAD_ATTR 1 (append) 31 LOAD_FAST 1 (i) 34 CALL_FUNCTION 1 (1 positional, 0 keyword pair) 37 POP_TOP 38 JUMP_ABSOLUTE 19 >> 41 POP_BLOCK >> 42 LOAD_CONST 0 (None) 45 RETURN_VALUE In [5]: dis.dis(f2) 2 0 LOAD_CONST 1 (<code object <listcomp> at 0x7fe48b2265d0, file "<ipython-input-3-9bc091d521d5>", line 2>) 3 LOAD_CONST 2 (''f2.<locals>.<listcomp>'') 6 MAKE_FUNCTION 0 9 LOAD_GLOBAL 0 (range) 12 LOAD_CONST 3 (5) 15 CALL_FUNCTION 1 (1 positional, 0 keyword pair) 18 GET_ITER 19 CALL_FUNCTION 1 (1 positional, 0 keyword pair) 22 POP_TOP 23 LOAD_CONST 0 (None) 26 RETURN_VALUE

Puede ver que en el desplazamiento 22 tenemos un atributo append en la primera función, ya que no tenemos tal cosa en la segunda función usando la comprensión de la lista. Todos esos códigos de bytes adicionales harán que el enfoque de anexión sea más lento. También tenga en cuenta que también tendrá que cargar el atributo append en cada iteración, lo que hace que su código tarde aproximadamente 2 veces más lento que la segunda función que utiliza la comprensión de la lista.