为什么Python在编译为字节码之前不评估常数运算?

Jon*_*han 43 python bytecode

在下面的代码中,为什么Python不能编译f2为相同的字节码f1

有理由不去吗?

>>> def f1(x):
    x*100

>>> dis.dis(f1)
  2           0 LOAD_FAST                0 (x)
              3 LOAD_CONST               1 (100)
              6 BINARY_MULTIPLY
              7 POP_TOP
              8 LOAD_CONST               0 (None)
             11 RETURN_VALUE
>>> def f2(x):
        x*10*10

>>> dis.dis(f2)
  2           0 LOAD_FAST                0 (x)
              3 LOAD_CONST               1 (10)
              6 BINARY_MULTIPLY
              7 LOAD_CONST               1 (10)
             10 BINARY_MULTIPLY
             11 POP_TOP
             12 LOAD_CONST               0 (None)
             15 RETURN_VALUE
Run Code Online (Sandbox Code Playgroud)

Nic*_*tin 71

这是因为x可能有一种__mul__副作用的方法. x * 10 * 10调用__mul__两次,而x * 100只调用一次:

>>> class Foo(object):
...     def __init__ (self):
...             self.val = 5
...     def __mul__ (self, other):
...             print "Called __mul__: %s" % (other)
...             self.val = self.val * other
...             return self
... 
>>> a = Foo()
>>> a * 10 * 10
Called __mul__: 10
Called __mul__: 10
<__main__.Foo object at 0x1017c4990>
Run Code Online (Sandbox Code Playgroud)

自动折叠常量只调用__mul__一次可能会改变行为.

您可以通过重新排序操作来获得所需的优化,使得常量首先相乘(或者,如注释中所述,使用括号将它们分组,使得它们仅在一起操作,无论位置如何),从而明确表示您的渴望折叠发生:

>>> def f1(x):
...     return 10 * 10 * x
... 
>>> dis.dis(f1)
  2           0 LOAD_CONST               2 (100)
              3 LOAD_FAST                0 (x)
              6 BINARY_MULTIPLY     
              7 RETURN_VALUE 
Run Code Online (Sandbox Code Playgroud)

  • `x*(10*10)`也应该有效,并且更明确一些. (6认同)
  • @ SanjayT.Sharma:编译器仍然无法真正知道`x`是什么,所以它必须安全地播放它.Python的多功能内省和动态运行时修改功能可以在函数locals中更改`x`的类型. (5认同)

Wes*_*ley 17

Python 从左到右评估表达式.对于f2(),这意味着它将首先评估x*10然后将结果乘以10.尝试:

尝试:

def f2(x):
    10*10*x
Run Code Online (Sandbox Code Playgroud)

这应该优化.