Dos formas similares de verificar si una lista contiene un número impar:
any(x % 2 for x in a) any(True for x in a if x % 2) Resultados de tiempo con a = [0] * 10000000 (cinco intentos cada uno, tiempos en segundos):
0.60 0.60 0.60 0.61 0.63 any(x % 2 for x in a) 0.36 0.36 0.36 0.37 0.37 any(True for x in a if x % 2)¿Por qué la segunda forma es casi el doble de rápida?
Mi código de prueba:
from timeit import repeat setup = 'a = [0] * 10000000' expressions = [ 'any(x % 2 for x in a)', 'any(True for x in a if x % 2)', ] for expression in expressions: times = sorted(repeat(expression, setup, number=1)) print(*('%.2f ' % t for t in times), expression)El primer método envía todo a any() mientras que el segundo solo envía a any() cuando hay un número impar, por lo que any() tiene menos elementos para pasar.
(x % 2 for x in a) Este generador produce una serie de valores falsos hasta que produce un valor verdadero (si lo hace), momento en el que any dejará de iterar el generador y devolverá True .
(True for x in a if x % 2) Este generador solo producirá exactamente un valor True (si lo hace), momento en el que any detendrá la iteración y devolverá True .
El ir y venir adicional de ceder a any y luego obtener el siguiente valor del generador en el primer caso representa la sobrecarga.
TL; DR La versión lenta tiene que iterar sobre una larga secuencia de valores falsos antes de devolver False . La versión rápida "itera" sobre una secuencia vacía antes de hacer lo mismo. La diferencia es el tiempo que se tarda en construir la secuencia larga y falsa frente a la secuencia vacía.
Veamos el código de bytes generado por cada uno. He omitido la primera sección para cada uno, ya que son idénticos para ambos. Es solo el código de los generadores involucrados lo que necesitamos mirar.
In [5]: dis.dis('any(x%2 for x in a)') [...] Disassembly of <code object <genexpr> at 0x105e860e0, file "<dis>", line 1>: 1 0 LOAD_FAST 0 (.0) >> 2 FOR_ITER 14 (to 18) 4 STORE_FAST 1 (x) 6 LOAD_FAST 1 (x) 8 LOAD_CONST 0 (2) 10 BINARY_MODULO 12 YIELD_VALUE 14 POP_TOP 16 JUMP_ABSOLUTE 2 >> 18 LOAD_CONST 1 (None) 20 RETURN_VALUE In [6]: dis.dis('any(True for x in a if x % 2)') [...] Disassembly of <code object <genexpr> at 0x105d993a0, file "<dis>", line 1>: 1 0 LOAD_FAST 0 (.0) >> 2 FOR_ITER 18 (to 22) 4 STORE_FAST 1 (x) 6 LOAD_FAST 1 (x) 8 LOAD_CONST 0 (2) 10 BINARY_MODULO 12 POP_JUMP_IF_FALSE 2 14 LOAD_CONST 1 (True) 16 YIELD_VALUE 18 POP_TOP 20 JUMP_ABSOLUTE 2 >> 22 LOAD_CONST 2 (None) 24 RETURN_VALUE Ambos son idénticos hasta la instrucción BINARY_MODULO . Después de eso, la versión más lenta tiene que generar el valor resultante para que any lo consuma antes de continuar, mientras que el segundo código pasa inmediatamente al siguiente valor. Básicamente, el código más lento tiene que consumir una larga lista de valores falsos (es decir, distintos de cero) para determinar que no hay valores verdaderos. El código más rápido solo necesita consumir una lista vacía.