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

在下面的代码中,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
Jonathan asked 2020-08-01T11:57:55Z
2个解决方案
71 votes

这是因为__mul__可能具有__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>

自动折叠常量,仅调用一次__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 
Nick Bastin answered 2020-08-01T11:58:20Z
17 votes

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

尝试:

def f2(x):
    10*10*x

应该对此进行优化。

Wesley answered 2020-08-01T11:58:50Z
translate from https://stackoverflow.com:/questions/9391845/why-doesnt-python-evaluate-constant-number-arithmetic-before-compiling-to-bytec