Python: ¿Por qué la comprensión de la lista es más lenta que para el bucle?

Esencialmente, estas son las mismas funciones, excepto que la comprensión de lista utiliza la sum lugar de x=0; x+= x=0; x+= ya que el último no es compatible. ¿Por qué la comstackción de listas es algo 40% más lenta?

 #list comprehension def movingAverage(samples, n=3): return [float(sum(samples[ij] for j in range(n)))/n for i in range(n-1, len(samples))] #regular def moving_average(samples, n=3): l =[] for i in range(n-1, len(samples)): x= 0 for j in range(n): x+= samples[ij] l.append((float(x)/n)) return l 

Para cronometrar las entradas de muestra, utilicé variaciones en [i*random.random() for i in range(x)]

Está utilizando una expresión generadora en su lista de comprensión:

 sum(samples[ij] for j in range(n)) 

Las expresiones del generador requieren que se cree un nuevo marco cada vez que ejecute uno, al igual que una llamada de función. Esto es relativamente caro.

No necesitas usar una expresión generadora en absoluto; solo necesitas cortar la lista de samples :

 sum(samples[i - n + 1:i + 1]) 

Puede especificar un segundo argumento, un valor de start para la función sum() ; configúralo a 0.0 para obtener un resultado flotante:

 sum(samples[i - n + 1:i + 1], 0.0) 

Juntos, estos cambios hacen toda la diferencia:

 >>> from timeit import timeit >>> import random >>> testdata = [i*random.random() for i in range(1000)] >>> def slow_moving_average(samples, n=3): ... return [float(sum(samples[ij] for j in range(n)))/n for i in range(n-1, len(samples))] ... >>> def fast_moving_average(samples, n=3): ... return [sum(samples[i - n + 1:i + 1], 0.0) / n for i in range(n-1, len(samples))] ... >>> def verbose_moving_average(samples, n=3): ... l =[] ... for i in range(n-1, len(samples)): ... x = 0.0 ... for j in range(n): ... x+= samples[ij] ... l.append(x / n) ... return l ... >>> timeit('f(s)', 'from __main__ import verbose_moving_average as f, testdata as s', number=1000) 0.9375386269966839 >>> timeit('f(s)', 'from __main__ import slow_moving_average as f, testdata as s', number=1000) 1.9631599469939829 >>> timeit('f(s)', 'from __main__ import fast_moving_average as f, testdata as s', number=1000) 0.5647804250038462