Mientras perfilaba un fragmento de código Python ( python 2.6hasta 3.2), descubrí que el
strmétodo para convertir un objeto (en mi caso, un número entero) en una cadena es casi un orden de magnitud más lento que usar el formato de cadena.
Aquí está el punto de referencia
>>> from timeit import Timer
>>> Timer('str(100000)').timeit()
0.3145311339386332
>>> Timer('"%s"%100000').timeit()
0.03803517023435887
¿Alguien sabe por qué es así? ¿Me estoy perdiendo de algo?
python
string
performance
python-3.x
python-2.7
Luca Sbardella
fuente
fuente

'{}'.format(100000)Respuestas:
'%s' % 100000es evaluado por el compilador y es equivalente a una constante en tiempo de ejecución.>>> import dis >>> dis.dis(lambda: str(100000)) 8 0 LOAD_GLOBAL 0 (str) 3 LOAD_CONST 1 (100000) 6 CALL_FUNCTION 1 9 RETURN_VALUE >>> dis.dis(lambda: '%s' % 100000) 9 0 LOAD_CONST 3 ('100000') 3 RETURN_VALUE%con una expresión en tiempo de ejecución no es (significativamente) más rápido questr:>>> Timer('str(x)', 'x=100').timeit() 0.25641703605651855 >>> Timer('"%s" % x', 'x=100').timeit() 0.2169809341430664Tenga en cuenta que
strtodavía es un poco más lento, como dijo @DietrichEpp, esto se debe a questrimplica operaciones de búsqueda y llamada de función, mientras que se%compila en un solo código de bytes inmediato:>>> dis.dis(lambda x: str(x)) 9 0 LOAD_GLOBAL 0 (str) 3 LOAD_FAST 0 (x) 6 CALL_FUNCTION 1 9 RETURN_VALUE >>> dis.dis(lambda x: '%s' % x) 10 0 LOAD_CONST 1 ('%s') 3 LOAD_FAST 0 (x) 6 BINARY_MODULO 7 RETURN_VALUEPor supuesto, lo anterior es cierto para el sistema en el que probé (CPython 2.7); otras implementaciones pueden diferir.
fuente
str. Gracias por responder. No hay razón para cambiar el código en todas partes :-)stres un nombre que se puede vincular a algo diferente al tipo de cadena, pero el formato de cadena, es decir, elstr.__mod__método, no se puede reemplazar, lo que permite que el compilador realice la optimización. El compilador no hace mucho en cuanto a optimización, pero hace más de lo que podría pensar :)Una razón que me viene a la mente es el hecho de que
str(100000)implica una búsqueda global, pero"%s"%100000no es así. Lostrglobal debe buscarse en el ámbito global. Esto no explica toda la diferencia:>>> Timer('str(100000)').timeit() 0.2941889762878418 >>> Timer('x(100000)', 'x=str').timeit() 0.24904918670654297Como señaló thg435 ,
>>> Timer('"%s"%100000',).timeit() 0.034214019775390625 >>> Timer('"%s"%x','x=100000').timeit() 0.2940788269042969fuente