Python 中的幂运算符 (**) 翻译成什么?
What does the power operator (**) in Python translate into?
换句话说,这两个星号后面是什么?是单纯的乘以x还是别的?
作为后续问题,写2**3
好还是2*2*2
好。我问是因为我听说在 C++ 中最好不要使用 pow()
进行简单计算,因为它会调用一个函数。
**
运算符将在内部使用迭代函数(与内置 pow()
(Python docs) 的语义相同,这可能意味着它只是调用该函数) .
因此,如果您知道它的强大功能并且可以对其进行硬编码,那么使用 2*2*2
可能会比 2**3
快一点。这个跟函数有点关系,不过我觉得主要的性能问题是会用到循环。
请注意,当它像 2**3
一样简单时,用可读性更强的代码替换可读性更差的代码仍然很愚蠢,性能增益充其量是最小的。
来自the docs:
The power operator binds more tightly than unary operators on its left; it binds less tightly than unary operators on its right. The syntax is:
power ::= primary ["**" u_expr]
Thus, in an unparenthesized sequence of power and unary operators, the operators are evaluated from right to left (this does not constrain the evaluation order for the operands): -1**2
results in -1
.
The power operator has the same semantics as the built-in pow()
function, when called with two arguments: it yields its left argument raised to the power of its right argument.
这意味着,在 Python 中:2**2**3
被评估为 2**(2**3) = 2**8 = 256
。
在数学中,堆叠指数是从上到下应用的。如果不这样做,你只会得到指数的乘法:
(((2**3)**4)**5) = 2**(3*4*5)
只做乘法可能会快一点,但可读性要差得多。
如果您对内部结构感兴趣,我会反汇编指令以获取它映射到的 CPython 字节码。使用 Python3:
»»» def test():
return 2**3
...:
»»» dis.dis(test)
2 0 LOAD_CONST 3 (8)
3 RETURN_VALUE
好的,这似乎是在输入时就完成了计算,并存储了结果。对于 2*2*2,您将获得完全相同的 CPython 字节码(请随意尝试)。因此,对于计算结果为常量的表达式,您会得到相同的结果,这无关紧要。
如果你想要一个变量的幂怎么办?
现在你得到了两个不同的字节码:
»»» def test(n):
return n ** 3
»»» dis.dis(test)
2 0 LOAD_FAST 0 (n)
3 LOAD_CONST 1 (3)
6 BINARY_POWER
7 RETURN_VALUE
对比
»»» def test(n):
return n * 2 * 2
....:
»»» dis.dis(test)
2 0 LOAD_FAST 0 (n)
3 LOAD_CONST 1 (2)
6 BINARY_MULTIPLY
7 LOAD_CONST 1 (2)
10 BINARY_MULTIPLY
11 RETURN_VALUE
现在的问题当然是,BINARY_MULTIPLY 比 BINARY_POWER 操作快吗?
最好的尝试方法是使用 timeit。我将使用 IPython %timeit
魔法。这是乘法的输出:
%timeit test(100)
The slowest run took 15.52 times longer than the fastest. This could mean that an intermediate result is being cached
10000000 loops, best of 3: 163 ns per loop
和电源
The slowest run took 5.44 times longer than the fastest. This could mean that an intermediate result is being cached
1000000 loops, best of 3: 473 ns per loop
您可能希望对代表性输入重复此操作,但根据经验,乘法看起来更快(但请注意上述关于输出方差的警告)。
如果您想了解更多内部信息,我建议您深入研究 CPython 代码。
虽然第二个对于数字来说稍微快一点,但与第一个相比优势很低:可读性。如果您需要时间,并且被迫进行此类优化,那么 python 可能 不是您应该使用的语言。
注意:对于数字以外的值:
a ** b 转换为
a.__pow__(b)
而 a * a * a 是对
的调用
a.__mul__(a.__mul__(a))
测试代码:
import time
s = time.time()
for x in xrange(1,1000000):
x**5
print "done in ", time.time() - s
s = time.time()
for x in xrange(1,1000000):
x*x*x*x*x
print "done in ", time.time() - s
对于我的机器,它产生:
done in 0.975429058075
done in 0.260419845581
[Finished in 1.2s]
说白了,乘法快一点
>>timeit.timeit('[i*i*i*i for i in range(100)]', number=10000)
0.262529843304
>>timeit.timeit('[i**4 for i in range(100)]', number=10000)
0.31143438383
但是,当您从两个选项中选择一个时,速度并不是唯一要考虑的因素。例如,在计算 2 的 20 次方时,什么更容易?简单地编写 2**20
或使用将迭代 20 次并执行一些乘法任务的 for 循环?
换句话说,这两个星号后面是什么?是单纯的乘以x还是别的?
作为后续问题,写2**3
好还是2*2*2
好。我问是因为我听说在 C++ 中最好不要使用 pow()
进行简单计算,因为它会调用一个函数。
**
运算符将在内部使用迭代函数(与内置 pow()
(Python docs) 的语义相同,这可能意味着它只是调用该函数) .
因此,如果您知道它的强大功能并且可以对其进行硬编码,那么使用 2*2*2
可能会比 2**3
快一点。这个跟函数有点关系,不过我觉得主要的性能问题是会用到循环。
请注意,当它像 2**3
一样简单时,用可读性更强的代码替换可读性更差的代码仍然很愚蠢,性能增益充其量是最小的。
来自the docs:
The power operator binds more tightly than unary operators on its left; it binds less tightly than unary operators on its right. The syntax is:
power ::= primary ["**" u_expr]
Thus, in an unparenthesized sequence of power and unary operators, the operators are evaluated from right to left (this does not constrain the evaluation order for the operands):
-1**2
results in-1
.The power operator has the same semantics as the built-in
pow()
function, when called with two arguments: it yields its left argument raised to the power of its right argument.
这意味着,在 Python 中:2**2**3
被评估为 2**(2**3) = 2**8 = 256
。
在数学中,堆叠指数是从上到下应用的。如果不这样做,你只会得到指数的乘法:
(((2**3)**4)**5) = 2**(3*4*5)
只做乘法可能会快一点,但可读性要差得多。
如果您对内部结构感兴趣,我会反汇编指令以获取它映射到的 CPython 字节码。使用 Python3:
»»» def test():
return 2**3
...:
»»» dis.dis(test)
2 0 LOAD_CONST 3 (8)
3 RETURN_VALUE
好的,这似乎是在输入时就完成了计算,并存储了结果。对于 2*2*2,您将获得完全相同的 CPython 字节码(请随意尝试)。因此,对于计算结果为常量的表达式,您会得到相同的结果,这无关紧要。
如果你想要一个变量的幂怎么办?
现在你得到了两个不同的字节码:
»»» def test(n):
return n ** 3
»»» dis.dis(test)
2 0 LOAD_FAST 0 (n)
3 LOAD_CONST 1 (3)
6 BINARY_POWER
7 RETURN_VALUE
对比
»»» def test(n):
return n * 2 * 2
....:
»»» dis.dis(test)
2 0 LOAD_FAST 0 (n)
3 LOAD_CONST 1 (2)
6 BINARY_MULTIPLY
7 LOAD_CONST 1 (2)
10 BINARY_MULTIPLY
11 RETURN_VALUE
现在的问题当然是,BINARY_MULTIPLY 比 BINARY_POWER 操作快吗?
最好的尝试方法是使用 timeit。我将使用 IPython %timeit
魔法。这是乘法的输出:
%timeit test(100)
The slowest run took 15.52 times longer than the fastest. This could mean that an intermediate result is being cached
10000000 loops, best of 3: 163 ns per loop
和电源
The slowest run took 5.44 times longer than the fastest. This could mean that an intermediate result is being cached
1000000 loops, best of 3: 473 ns per loop
您可能希望对代表性输入重复此操作,但根据经验,乘法看起来更快(但请注意上述关于输出方差的警告)。
如果您想了解更多内部信息,我建议您深入研究 CPython 代码。
虽然第二个对于数字来说稍微快一点,但与第一个相比优势很低:可读性。如果您需要时间,并且被迫进行此类优化,那么 python 可能 不是您应该使用的语言。
注意:对于数字以外的值:
a ** b 转换为
a.__pow__(b)
而 a * a * a 是对
的调用a.__mul__(a.__mul__(a))
测试代码:
import time
s = time.time()
for x in xrange(1,1000000):
x**5
print "done in ", time.time() - s
s = time.time()
for x in xrange(1,1000000):
x*x*x*x*x
print "done in ", time.time() - s
对于我的机器,它产生:
done in 0.975429058075
done in 0.260419845581
[Finished in 1.2s]
说白了,乘法快一点
>>timeit.timeit('[i*i*i*i for i in range(100)]', number=10000)
0.262529843304
>>timeit.timeit('[i**4 for i in range(100)]', number=10000)
0.31143438383
但是,当您从两个选项中选择一个时,速度并不是唯一要考虑的因素。例如,在计算 2 的 20 次方时,什么更容易?简单地编写 2**20
或使用将迭代 20 次并执行一些乘法任务的 for 循环?