No puedo creer lo que acabo de medir:
python3 -m timeit -s "from math import sqrt" "sqrt(2)" 5000000 loops, best of 5: 42.8 nsec per loop python3 -m timeit "2 ** 0.5" 50000000 loops, best of 5: 4.93 nsec per loopEsto va en contra de cualquier intuición... ¡debería ser exactamente lo contrario!
Python 3.8.3 en macOS Catalina
Python 3 calcula previamente el valor de 2 ** 0.5 en tiempo de compilación, ya que ambos operandos se conocen en ese momento. Sin embargo, el valor de sqrt no se conoce en tiempo de compilación, por lo que el cálculo necesariamente ocurre en tiempo de ejecución.
No está midiendo cuánto tiempo lleva calcular 2 ** 0.5 , sino solo el tiempo que lleva cargar una constante.
Una comparación más justa sería
$ python3 -m timeit -s "from math import sqrt" "sqrt(2)" 5000000 loops, best of 5: 50.7 nsec per loop $ python3 -m timeit -s "x = 2" "x**0.5" 5000000 loops, best of 5: 56.7 nsec per loopNo estoy seguro de si hay una forma de mostrar el código de bytes no optimizado. Python comienza analizando el código fuente en un árbol de sintaxis abstracta (AST):
>>> ast.dump(ast.parse("2**0.5")) 'Module(body=[Expr(value=BinOp(left=Num(n=2), op=Pow(), right=Num(n=0.5)))])'Actualización : esta optimización particular ahora se aplica directamente al árbol de sintaxis abstracta , por lo que el código de bytes se genera directamente desde algo como
Module(body=Num(n= 1.4142135623730951)) El módulo ast no parece aplicar la optimización.
El compilador toma el AST y genera un código de bytes no optimizado; en este caso, creo que se vería (según la salida de dis.dis("2**x") y dis.dis("x**0.5") ) como
LOAD_CONST 0 (2) LOAD_CONST 1 (0.5) BINARY_POWER RETURN_VALUE El código de bytes sin procesar está sujeto a modificaciones por parte del optimizador de mirilla, que puede reducir estas 4 instrucciones a 2, como se muestra en el módulo dis .
Luego, el compilador genera un código de bytes a partir del AST.
>>> dis.dis("2**0.5") 1 0 LOAD_CONST 0 (1.4142135623730951) 2 RETURN_VALUE[Si bien el siguiente párrafo se escribió originalmente con la idea de optimizar el código de bytes en mente, el razonamiento también se aplica a la optimización del AST].
Dado que nada en el tiempo de ejecución afecta cómo se evalúan las dos LOAD_CONST y BINARY_POWER (por ejemplo, no hay búsquedas de nombres), el optimizador de mirilla puede tomar esta secuencia de códigos de bytes, realizar el cálculo de 2**0.5 y reemplazar el primeras tres instrucciones con una sola instrucción LOAD_CONST que carga el resultado inmediatamente.
Para mejorar la respuesta de Chepner , aquí hay una prueba:
Python 3.5.3 (default, Sep 27 2018, 17:25:39) [GCC 6.3.0 20170516] on linux Type "help", "copyright", "credits" or "license" for more information. >>> import dis >>> dis.dis('2 ** 0.5') 1 0 LOAD_CONST 2 (1.4142135623730951) 3 RETURN_VALUEcontra
>>> dis.dis('sqrt(2)') 1 0 LOAD_NAME 0 (sqrt) 3 LOAD_CONST 0 (2) 6 CALL_FUNCTION 1 (1 positional, 0 keyword pair) 9 RETURN_VALUE>>> dis.dis('44442.3123 ** 0.5') 0 LOAD_CONST 0 (210.81345379268373) 2 RETURN_VALUE No creo que 44442.3123 ** 0.5 esté precalculado en tiempo de compilación. Deberíamos comprobar mejor el AST del código.
>>> import ast >>> import math >>> code = ast.parse("2**2") >>> ast.dump(code) 'Module(body=[Expr(value=BinOp(left=Num(n=2), op=Pow(), right=Num(n=2)))])' >>> code = ast.parse("math.sqrt(3)") >>> ast.dump(code) "Module(body=[Expr(value=Call(func=Attribute(value=Name(id='math', ctx=Load()), attr='sqrt', ctx=Load()), args=[Num(n=3)], keywords=[]))])"