<p>Python3在编译时预计算<code>2 ** 0.5</code>的值,因为两个操作数在当时都是已知的。但是<code>sqrt</code>的值在编译时是未知的,因此计算必须在运行时进行</p>
<p>计算<code>2 ** 0.5</code>所需的时间不是计时,而是加载常数所需的时间</p>
<p>更公平的比较是</p>
<pre><code>$ 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 loop
</code></pre>
<hr/>
<p>我不确定是否有办法显示未优化的字节码。Python首先将源代码解析为抽象语法树(AST):</p>
<pre><code>>>> ast.dump(ast.parse("2**0.5"))
'Module(body=[Expr(value=BinOp(left=Num(n=2), op=Pow(), right=Num(n=0.5)))])'
</code></pre>
<p><strong>更新</strong>:这个特定的优化现在被应用<a href="https://github.com/python/cpython/blob/v3.8.3/Python/ast_opt.c#L214" rel="noreferrer">directly to the abstract syntax tree</a>,因此字节码是直接从以下内容生成的</p>
<pre><code>Module(body=Num(n= 1.4142135623730951))
</code></pre>
<p><code>ast</code>模块似乎没有应用优化</p>
<p><strike>编译器接受AST并生成未优化的字节码;在这种情况下,我相信它看起来(基于<code>dis.dis("2**x")</code>和<code>dis.dis("x**0.5")</code>的输出)像</strike></p>
<pre><code>LOAD_CONST 0 (2)
LOAD_CONST 1 (0.5)
BINARY_POWER
RETURN_VALUE
</code></pre>
<p>原始字节码然后由窥视孔优化程序修改,它可以将这4条指令减少为2条,如<code>dis</code>模块所示</p>
<p>然后,编译器从AST生成字节码</p>
<pre><code>>>> dis.dis("2**0.5")
1 0 LOAD_CONST 0 (1.4142135623730951)
2 RETURN_VALUE
</code></pre>
<p>[虽然以下段落最初是为了优化字节码而编写的,但推理也适用于优化AST。]</p>
<p>由于运行时没有任何东西会影响两条<code>LOAD_CONST</code>和后面的<code>BINARY_POWER</code>指令的计算方式(例如,没有名称查找),因此窥视孔优化器可以获取这一字节码序列,执行<code>2**0.5</code>本身的计算,并用一条<code>LOAD_CONST</code>替换前三条指令立即加载结果的指令</p>