【问题标题】:What does the power operator (**) in Python translate into?Python 中的幂运算符 (**) 翻译成什么?
【发布时间】:2016-02-19 03:48:54
【问题描述】:

换句话说,两个星号后面是什么?是简单地将数字乘以 x 还是别的什么?

作为后续问题,写2**3 还是2*2*2 更好。我问是因为我听说在 C++ 中最好不要使用 pow() 进行简单计算,因为它调用了一个函数。

【问题讨论】:

  • 2**3 而不是2*2*2。它更具可读性。
  • 这并不能真正回答我的问题
  • 这就是为什么它是一个评论,而不是一个答案;)
  • 这对我来说似乎是一个答案。 2**3 更好因为它更具可读性。您的意思是问“写2**3 是否更快”?
  • C++ pow() 的问题不在于它是一个函数(如今大多数 C++ 编译器都可以在启用优化的情况下内联简单函数,即使该函数没有明确标记为 inline ),问题在于它是为浮点取幂而设计的,所以它对整数来说是多余的。如果您想要仅整数 pow,请参阅 stackoverflow.com/questions/101439

标签: python operators translate


【解决方案1】:

** 运算符将在内部使用迭代函数(与内置 pow() (Python docs) 的语义相同,这可能意味着它无论如何都只是调用该函数)。

因此,如果您知道功能并且可以对其进行硬编码,那么使用2*2*2 可能会比2**3 快一点。这与函数有点关系,但我认为主要的性能问题是它会使用循环。


请注意,当像2**3 这样简单的东西时,用可读性更高的代码替换可读性更低的代码仍然很愚蠢,性能提升最多是最小的。

【讨论】:

  • 您的结束评论是这里的关键。 假设 2*2*22**3 稍微快一点,应该强调的是,如果您发现自己需要在 Python 代码中使用这种微优化,那么 Python 可能是错误的语言选择.
【解决方案2】:

来自the docs

幂运算符比左侧的一元运算符绑定得更紧密;它的绑定不如右边的一元运算符那么紧密。语法是:

power ::=  primary ["**" u_expr]

因此,在无括号的幂运算符和一元运算符序列中,运算符的计算顺序是从右到左(这不会限制操作数的计算顺序):-1**2 结果为 -1

幂运算符与内置 pow() 函数具有相同的语义,当使用两个参数调用时:它会产生其左参数的右参数的幂。

这意味着,在 Python 中:2**2**3 被评估为 2**(2**3) = 2**8 = 256

在数学中,堆叠指数是自上而下应用的。如果不这样做,你只会得到指数的乘法:

(((2**3)**4)**5) = 2**(3*4*5)

仅仅做乘法可能会快一点,但可读性要差得多。

【讨论】:

    【解决方案3】:

    如果您对内部结构感兴趣,我会反汇编指令以获取它映射到的 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 代码。

    【讨论】:

    • 我认为您的测试函数不应该使用 return 语句,而应该只使用操作本身。我很烦人,我知道。
    • 我实际上首先尝试过,这里是第一个示例转换为没有返回的指令:LOAD_CONST、POP_TOP、LOAD_CONST、RETURN_VALUE。我不确定这对分析有多大帮助。
    • 介于返回 NullObject 或 int 之间。我宁愿返回 Null。
    • 这个答案是一个巨大的帮助,解释了为什么我在%timeit 10**13 上得到了非常快的数字,但在i = 10; %timeit i**13 上却慢了很多,并且在 Python 的 longobject.c 代码中找不到任何可以解释的优化它!
    【解决方案4】:

    虽然第二个处理数字的速度有点快,但与第一个相比,优势非常低:可读性。如果您赶时间,并且迫于压力进行此类优化,那么 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]
    

    【讨论】:

      【解决方案5】:

      如果你坦率地问,乘法会快一点。

      >>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 或使用for 循环来迭代20 次并执行一些乘法任务?

      【讨论】:

        猜你喜欢
        • 2018-03-08
        • 1970-01-01
        • 2017-06-06
        • 1970-01-01
        • 2021-10-28
        • 2018-08-02
        • 2013-01-15
        • 1970-01-01
        相关资源
        最近更新 更多