Aquí hay dos medidas:
timeit.timeit('"toto"=="1234"', number=100000000) 1.8320042459999968 timeit.timeit('"toto"=="toto"', number=100000000) 1.4517491540000265 Como puede ver, comparar dos cadenas que coinciden es más rápido que comparar dos cadenas del mismo tamaño que no coinciden. Esto es bastante perturbador: durante una comparación de cadenas, creí que Python estaba probando cadenas carácter por carácter, por lo que "toto"=="toto" debería ser más largo para probar que "toto"=="1234" ya que requiere cuatro pruebas contra uno para la comparación no coincidente. Tal vez la comparación esté basada en hash, pero en este caso, los tiempos deberían ser los mismos para ambas comparaciones.
¿Por qué?
Combinando mi comentario y el comentario de @khelwood:
TL;RD:
Al analizar el código de bytes para las dos comparaciones, revela que las cadenas de 'time' y 'time' están asignadas al mismo objeto. Por lo tanto, una verificación de identidad por adelantado (en el nivel C) es la razón del aumento de la velocidad de comparación.
El motivo de la misma asignación de objeto es que, como detalle de implementación , CPython interna cadenas que contienen solo 'caracteres de nombre' (es decir, caracteres alfabéticos y de subrayado). Esto habilita la verificación de identidad del objeto.
Código de bytes:
import dis In [24]: dis.dis("'time'=='time'") 1 0 LOAD_CONST 0 ('time') # <-- same object (0) 2 LOAD_CONST 0 ('time') # <-- same object (0) 4 COMPARE_OP 2 (==) 6 RETURN_VALUE In [25]: dis.dis("'time'=='1234'") 1 0 LOAD_CONST 0 ('time') # <-- different object (0) 2 LOAD_CONST 1 ('1234') # <-- different object (1) 4 COMPARE_OP 2 (==) 6 RETURN_VALUETiempo de asignación:
La 'aceleración' también se puede ver al usar la asignación para las pruebas de tiempo. La asignación (y comparación) de dos variables a la misma cadena es más rápida que la asignación (y comparación) de dos variables a cadenas diferentes. Apoyando aún más la hipótesis, la lógica subyacente está realizando una comparación de objetos. Esto se confirma en la siguiente sección.
In [26]: timeit.timeit("x='time'; y='time'; x==y", number=1000000) Out[26]: 0.0745926329982467 In [27]: timeit.timeit("x='time'; y='1234'; x==y", number=1000000) Out[27]: 0.10328884399496019Código fuente de Python:
Tal como lo proporcionaron amablemente @mkrieger1 y @Masklinn en sus comentarios, el código fuente de unicodeobject.c realiza primero una comparación de punteros y, si es True , regresa inmediatamente.
int _PyUnicode_Equal(PyObject *str1, PyObject *str2) { assert(PyUnicode_CheckExact(str1)); assert(PyUnicode_CheckExact(str2)); if (str1 == str2) { // <-- Here return 1; } if (PyUnicode_READY(str1) || PyUnicode_READY(str2)) { return -1; } return unicode_compare_eq(str1, str2); }Apéndice:
No siempre es más rápido comparar cadenas que coinciden. En cambio, siempre es más rápido comparar cadenas que comparten la misma identificación. Una prueba de que la identidad es de hecho la razón de este comportamiento (como @S3DEV ha explicado brillantemente) es esta:
>>> x = 'toto' >>> y = 'toto' >>> z = 'totoo'[:-1] >>> w = 'abcd' >>> x == y True >>> x == z True >>> x == w False >>> id(x) == id(y) True >>> id(x) == id(z) False >>> id(x) == id(w) False >>> timeit.timeit('x==y', number=100000000, globals={'x': x, 'y': y}) 3.893762200000083 >>> timeit.timeit('x==z', number=100000000, globals={'x': x, 'z': z}) 4.205321462000029 >>> timeit.timeit('x==w', number=100000000, globals={'x': x, 'w': w}) 4.15288594499998 Siempre es más rápido comparar objetos que tienen la misma identificación (como puede notar en el ejemplo, la comparación entre x y z es más lenta en comparación con la comparación entre x e y , y eso se debe a que x y z no comparten la misma identificación).