使用 math.pow和 **操作符哪一个更有效? 什么时候应该使用一个而不是另一个?
math.pow
**
到目前为止,我知道如果使用小数,x**y可以返回 int或 float 函数 pow将返回一个浮点数
x**y
int
float
pow
import math print( math.pow(10, 2) ) print( 10. ** 2 )
它们是用来完成不同的任务的,真的。
如果需要整数算术,请使用 pow(相当于带有两个参数的 x ** y)。
x ** y
如果任何一个参数是 float,并且您想要浮点数输出,则使用 math.pow。
有关 pow和 math.pow之间的差异的讨论,请参阅 有个问题。
使用电源操作符 **会更快,因为它不会有函数调用的开销。如果您反汇编 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这样的常量表达式实际上是在编译时计算的。
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的速度大约是它们的两倍,但它们仍然足够快,不会太在意。除非您能够将指数化确定为一个瓶颈,否则,如果清晰度降低,就没有理由选择一种方法而不选择另一种方法。这一点尤其适用,因为例如,pow提供了一个集成的模除。
阿尔菲在上面的评论中提出了一个很好的问题:
timeit显示 math.pow在所有情况下都慢于 **。math.pow()到底有什么用?有人知道它在哪里能发挥作用吗?
timeit
math.pow()
与内置的 pow和电源操作符 **相比,math.pow的最大区别在于 一直都是使用浮点语义。因此,如果出于某种原因,希望确保返回一个 float,那么 math.pow将确保该属性。
让我们想一个例子: 我们有两个数字,i和 j,并且不知道它们是浮点数还是整数。但是我们希望得到 i^j的浮点结果。那我们还有什么选择?
j
i^j
i ** j
那么,让我们测试一下:
>>> 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()。
float()
此外,值得注意的是,通过为自定义类型实现特殊的 __pow__(和 __rpow__)方法,可以覆盖 **和 pow的行为。因此,如果你不想(不管什么原因) ,使用 math.pow将不会这样做。
__pow__
__rpow__
仅就协议而言: **操作符相当于 内置的 pow函数的双参数版本,如果前两个参数是整数,pow 功能接受可选的第三个参数(模)。
因此,如果您打算从幂中计算余数,请使用内置函数。对于合理大小的参数,math.pow会给出错误的结果:
import math base = 13 exp = 100 mod = 2 print math.pow(base, exp) % mod print pow(base, exp, mod)
当我运行这个函数时,我在第一种情况下得到了 0.0,这显然不可能是真的,因为13是奇数(因此它的所有积分幂都是奇数)。math.pow版本使用了有限的精度的 IEEE-754 双精度(52位尾数,略低于16位小数位) ,这导致了一个错误在这里。
0.0
为了公平起见,我们必须说,math.pow 可以也更快:
>>> import timeit >>> min(timeit.repeat("pow(1.1, 9.9)", number=2000000, repeat=5)) 0.3063715160001266 >>> min(timeit.repeat("math.pow(1.1, 9.9)", setup="import math", number=2000000, repeat=5)) 0.2647279420000359
math.pow函数在工程应用中具有(并且仍然具有)优势,但是对于大量的理论应用,您应该使用内置的 pow函数。
一些网上的例子
更新(不可避免的修正) : 我删除了 math.pow(2,100)和 pow(2,100)的时间比较,因为 math.pow给出了错误的结果,而例如,pow(2,50)和 math.pow(2,50)之间的比较将是公平的(尽管不是 math模块功能的现实使用)。我添加了一个更好的一个和细节,导致限制的 math.pow。
math.pow(2,100)
pow(2,100)
pow(2,50)
math.pow(2,50)
math
**确实比 math.pow()快,但是如果你想要一个简单的二次函数,比如在你的例子中,使用一个积更快。
10.*10.
会更快
10.**2
对于一个操作(使用 timeit) ,这种差异并不大,也不明显,但对于大量操作,这种差异可能非常显著。
Pow ()函数允许您添加第三个参数作为模。
例如: 我最近在做的时候遇到了一个记忆错误
2 * * 23375247598357347582% 23375247598357347583
相反,我做到了:
pow(2, 23375247598357347582, 23375247598357347583)
这只需要几毫秒就可以返回,而不是普通指数所需要的大量时间和内存。因此,当处理大数和平行模数时,pow ()更有效,但是当处理没有模数的小数时,* * 更有效。