使用power运算符**将更快,因为它没有函数调用的开销。如果反汇编Python代码,可以看到这一点:>>> dis.dis('7. ** i')
1 0 LOAD_CONST 0 (7.0)
3 LOAD_NAME 0 (i)
6 BINARY_POWER
7 RETURN_VALUE
>>> dis.dis('pow(7., i)')
1 0 LOAD_NAME 0 (pow)
3 LOAD_CONST 0 (7.0)
6 LOAD_NAME 1 (i)
9 CALL_FUNCTION 2 (2 positional, 0 keyword pair)
12 RETURN_VALUE
>>> dis.dis('math.pow(7, i)')
1 0 LOAD_NAME 0 (math)
3 LOAD_ATTR 1 (pow)
6 LOAD_CONST 0 (7)
9 LOAD_NAME 2 (i)
12 CALL_FUNCTION 2 (2 positional, 0 keyword pair)
15 RETURN_VALUE
注意,我在这里使用变量i作为指数,因为像7. ** 5这样的常量表达式实际上是在编译时计算的。
现在,在实践中,这种差异并没有那么重要,正如你在计时时所看到的:>>> from timeit import timeit
>>> timeit('7. ** i', setup='i = 5')
0.2894785532627111
>>> timeit('pow(7., i)', setup='i = 5')
0.41218495570683444
>>> timeit('math.pow(7, i)', setup='import math; i = 5')
0.5655053168791255
所以,虽然pow和math.pow的速度大约是后者的两倍,但它们仍然足够快,不需要太多关注。除非你真的能将指数化识别为一个瓶颈,否则如果清晰度降低,就没有理由选择一种方法而不是另一种方法。这尤其适用,因为^{}提供了一个集成的模操作。
阿尔夫在上面的评论中问了一个好问题:timeit shows that math.pow is slower than ** in all cases. What is math.pow() good for anyway? Has anybody an idea where it can be of any advantage then?
与内置的pow和幂运算符**的最大区别在于它始终使用浮点语义。因此,如果出于某种原因,您想确保返回的结果是float,那么math.pow将确保此属性。
举个例子:我们有两个数字,i和j,不知道它们是浮点数还是整数。但是我们想要一个i^j的浮点结果。我们有什么选择?我们至少可以将其中一个参数转换为浮点,然后执行i ** j。
我们可以做i ** j并将结果转换为浮点(当i或j是浮点时,会自动使用浮点指数,因此结果是相同的)。
我们可以使用math.pow。
那么,让我们来测试一下:>>> timeit('float(i) ** j', setup='i, j = 7, 5')
0.7610865891750791
>>> timeit('i ** float(j)', setup='i, j = 7, 5')
0.7930400942188385
>>> timeit('float(i ** j)', setup='i, j = 7, 5')
0.8946636625872202
>>> timeit('math.pow(i, j)', setup='import math; i, j = 7, 5')
0.5699394063529439
如您所见,math.pow实际上更快!如果你仔细想想,函数调用的开销现在也没有了,因为在所有其他选择中,我们都必须调用float()。
此外,值得注意的是,**和pow的行为可以通过为自定义类型实现特殊的__pow__(和__rpow__)方法来重写。因此,如果您不想这样做(无论出于什么原因),那么使用math.pow就不会这样做。