¿Cuál es el mejor enfoque en python: multiple OR o IN en la sentencia if?

¿Cuál es el mejor enfoque en python: multiple OR o IN en la sentencia if? Teniendo en cuenta el rendimiento y las mejores prácticas.

if cond == '1' or cond == '2' or cond == '3' or cond == '4' (etc...) : 

O

 if cond in ['1','2','3','4']: 

Gracias.

El mejor enfoque es utilizar un conjunto :

 if cond in {'1','2','3','4'}: 

como prueba de membresía en un conjunto es O (1) (costo constante).

Los otros dos enfoques son iguales en complejidad; Simplemente una diferencia en los costos constantes. Tanto la prueba en una lista como el cortocircuito or cadena; termina tan pronto como se encuentre una coincidencia. Uno usa una secuencia de saltos de código byte (salta al final si es True ), el otro usa un bucle C y una salida temprana si el valor coincide. En el peor de los casos, donde cond no coincide con un elemento en la secuencia, cualquiera de los dos enfoques debe verificar todos los elementos antes de que pueda devolver False . De los dos, escogería el in cualquier día porque es mucho más legible.

La respuesta de Pieters es la mejor en la mayoría de los casos. Sin embargo, en su caso específico, no lo usaría in o or sino que hago esto:

 if 0 < int(cond) < 5: 

Si cond es '1', '2', '3' o '4', el bloque if se ejecutará. Lo bueno de esto es que es más corto que las otras respuestas.

Esto realmente depende de la versión de Python. En Python 2.7 no había constantes establecidas en el bytecode, por lo tanto, en Python 2, en el caso de una constante fija, un pequeño conjunto de valores usa una tupla:

 if x in ('2', '3', '5', '7'): ... 

Una tupla es una constante:

 >>> dis.dis(lambda: item in ('1','2','3','4')) 1 0 LOAD_GLOBAL 0 (item) 3 LOAD_CONST 5 (('1', '2', '3', '4')) 6 COMPARE_OP 6 (in) 9 RETURN_VALUE 

Python también es lo suficientemente inteligente como para optimizar una lista constante en Python 2.7 para una tupla:

 >>> dis.dis(lambda: item in ['1','2','3','4']) 1 0 LOAD_GLOBAL 0 (item) 3 LOAD_CONST 5 (('1', '2', '3', '4')) 6 COMPARE_OP 6 (in) 9 RETURN_VALUE 

Pero el bytecode (y el comstackdor) de Python 2.7 carece de soporte para conjuntos constantes:

 >>> dis.dis(lambda: item in {'1','2','3','4'}) 1 0 LOAD_GLOBAL 0 (item) 3 LOAD_CONST 1 ('1') 6 LOAD_CONST 2 ('2') 9 LOAD_CONST 3 ('3') 12 LOAD_CONST 4 ('4') 15 BUILD_SET 4 18 COMPARE_OP 6 (in) 21 RETURN_VALUE 

Lo que significa que la condición establecida en debe ser reconstruida para cada prueba .


Sin embargo, en Python 3.4 el bytecode soporta constantes establecidas; Allí el código evalúa a:

 >>> dis.dis(lambda: item in {'1','2','3','4'}) 1 0 LOAD_GLOBAL 0 (item) 3 LOAD_CONST 5 (frozenset({'4', '2', '1', '3'})) 6 COMPARE_OP 6 (in) 9 RETURN_VALUE 

En cuanto al código or código múltiple, produce un código de bytes totalmente espantoso:

 >>> dis.dis(lambda: item == '1' or item == '2' or item == '3' or item == '4') 1 0 LOAD_GLOBAL 0 (item) 3 LOAD_CONST 1 ('1') 6 COMPARE_OP 2 (==) 9 JUMP_IF_TRUE_OR_POP 45 12 LOAD_GLOBAL 0 (item) 15 LOAD_CONST 2 ('2') 18 COMPARE_OP 2 (==) 21 JUMP_IF_TRUE_OR_POP 45 24 LOAD_GLOBAL 0 (item) 27 LOAD_CONST 3 ('3') 30 COMPARE_OP 2 (==) 33 JUMP_IF_TRUE_OR_POP 45 36 LOAD_GLOBAL 0 (item) 39 LOAD_CONST 4 ('4') 42 COMPARE_OP 2 (==) >> 45 RETURN_VALUE