float - python string format replace
Python si no== vs if!= (7)
¿Cuál es la diferencia entre estas dos líneas de código?
if not x == ''val'':
y
if x != ''val'':
¿Es uno más eficiente que el otro?
¿Sería mejor usar
if x == ''val'':
pass
else:
@jonrsharpe tiene una excelente explicación de lo que está sucediendo. Pensé que solo mostraría la diferencia en el tiempo al ejecutar cada una de las 3 opciones 10,000,000 de veces (suficiente para mostrar una ligera diferencia).
Código usado:
def a(x):
if x != ''val'':
pass
def b(x):
if not x == ''val'':
pass
def c(x):
if x == ''val'':
pass
else:
pass
x = 1
for i in range(10000000):
a(x)
b(x)
c(x)
Y los resultados del perfilador cProfile:
Entonces podemos ver que hay una diferencia muy pequeña de ~ 0.7% entre
if not x == ''val'':
y
if x != ''val'':
De estos,
if x != ''val'':
es el más rápido.
Sin embargo, lo más sorprendente es que podemos ver que
if x == ''val'':
pass
else:
es, de hecho, el más rápido, y late
if x != ''val'':
en ~ 0.3%.
Esto no es muy legible, pero supongo que si quisieras una mejora insignificante del rendimiento, uno podría seguir esta ruta.
En el primero, Python tiene que ejecutar una operación más de la necesaria (en lugar de simplemente verificar que no sea igual, tiene que verificar si no es cierto que sea igual, por lo tanto, una operación más). Sería imposible distinguir la diferencia de una ejecución, pero si se ejecuta muchas veces, la segunda sería más eficiente. En general, usaría el segundo, pero matemáticamente son lo mismo
Quiero ampliar mi comentario de legibilidad anterior.
Nuevamente, estoy completamente de acuerdo con la legibilidad que anula otras preocupaciones (insignificantes en cuanto al rendimiento).
Lo que me gustaría señalar es que el cerebro interpreta "positivo" más rápido que "negativo". Por ejemplo, "parar" frente a "no ir" (un ejemplo bastante pésimo debido a la diferencia en el número de palabras).
Entonces, dado una opción:
if a == b
(do this)
else
(do that)
es preferible al equivalente funcional:
if a != b
(do that)
else
(do this)
Menos legibilidad / comprensibilidad conduce a más errores. Quizás no en la codificación inicial, pero el mantenimiento (¡no tan inteligente como usted!) Cambia ...
Se trata de tu forma de leerlo.
not
operador es dinámico, es por eso que puede aplicarlo en
if not x == ''val'':
Pero
!=
Podría leerse en un mejor contexto como un operador que hace lo contrario de lo que
==
hace.
Una nota adicional, dado que las otras respuestas respondieron su pregunta en su mayoría correctamente, es que si una clase solo define
__eq__()
y no
__ne__()
, entonces
COMPARE_OP (!=)
Ejecutará
__eq__()
y lo
__eq__()
.
En ese momento, es probable que su tercera opción sea un poco más eficiente, pero solo debe considerarse si NECESITA la velocidad, ya que es difícil de entender rápidamente.
Usando
dis
para mirar el bytecode generado para las dos versiones:
not ==
4 0 LOAD_FAST 0 (foo)
3 LOAD_FAST 1 (bar)
6 COMPARE_OP 2 (==)
9 UNARY_NOT
10 RETURN_VALUE
!=
4 0 LOAD_FAST 0 (foo)
3 LOAD_FAST 1 (bar)
6 COMPARE_OP 3 (!=)
9 RETURN_VALUE
Este último tiene menos operaciones y, por lo tanto, es probable que sea un poco más eficiente.
Se señaló
en los comentarios
(gracias,
@Quincunx
) que donde tienes
if foo != bar
vs.
if not foo == bar
el número de operaciones es exactamente el mismo, es solo que
COMPARE_OP
cambia y
POP_JUMP_IF_TRUE
cambia a
POP_JUMP_IF_FALSE
:
not ==
:
2 0 LOAD_FAST 0 (foo)
3 LOAD_FAST 1 (bar)
6 COMPARE_OP 2 (==)
9 POP_JUMP_IF_TRUE 16
!=
2 0 LOAD_FAST 0 (foo)
3 LOAD_FAST 1 (bar)
6 COMPARE_OP 3 (!=)
9 POP_JUMP_IF_FALSE 16
En este caso, a menos que haya una diferencia en la cantidad de trabajo requerido para cada comparación, es poco probable que vea alguna diferencia de rendimiento.
Sin embargo, tenga en cuenta que las dos versiones
no siempre serán lógicamente idénticas
, ya que dependerá de las implementaciones de
__eq__
y
__ne__
para los objetos en cuestión.
Según
la documentación del modelo de datos
:
No hay relaciones implícitas entre los operadores de comparación. La verdad de
x==y
no implica quex!=y
sea falso.
Por ejemplo:
>>> class Dummy(object):
def __eq__(self, other):
return True
def __ne__(self, other):
return True
>>> not Dummy() == Dummy()
False
>>> Dummy() != Dummy()
True
Finalmente, y quizás lo más importante: en general, donde los dos
son
lógicamente idénticos,
x != y
es mucho más legible que
not x == y
.
>>> from dis import dis
>>> dis(compile(''not 10 == 20'', '''', ''exec''))
1 0 LOAD_CONST 0 (10)
3 LOAD_CONST 1 (20)
6 COMPARE_OP 2 (==)
9 UNARY_NOT
10 POP_TOP
11 LOAD_CONST 2 (None)
14 RETURN_VALUE
>>> dis(compile(''10 != 20'', '''', ''exec''))
1 0 LOAD_CONST 0 (10)
3 LOAD_CONST 1 (20)
6 COMPARE_OP 3 (!=)
9 POP_TOP
10 LOAD_CONST 2 (None)
13 RETURN_VALUE
Aquí puede ver que
not x == y
tiene una instrucción más que
x != y
Por lo tanto, la diferencia de rendimiento será muy pequeña en la mayoría de los casos, a menos que esté haciendo millones de comparaciones e incluso entonces esto probablemente no sea la causa de un cuello de botella.