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

时间:2012-02-22 09:07:41

标签: 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

2 个答案:

答案 0 :(得分: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>

自动折叠常量,只调用__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 

答案 1 :(得分:17)

Python评估来自left to right的表达式。对于f2(),这意味着它将首先评估x*10,然后将结果乘以10.尝试:

尝试:

def f2(x):
    10*10*x

这应该优化。