【问题标题】:Why doesn't Python evaluate constant number arithmetic before compiling to bytecode?为什么 Python 在编译为字节码之前不评估常数算术?
【发布时间】:2012-03-12 14:33:37
【问题描述】:

在下面的代码中,为什么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

【问题讨论】:

    标签: python bytecode


    【解决方案1】:

    这是因为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__ 可以改变行为。

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

    >>> 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 
    

    【讨论】:

    • 请注意,即使是什么也没有传递给方法,dis 的结果仍然是相同的,即如果方法的内容是:x = 9; y = x * 10 * 10;,结果仍然是相同的即。加载 const 两次。看来Python没有做全方法优化?
    • @SanjayT.Sharma:编译器仍然无法真正知道x 是什么,所以它必须谨慎行事。 Python 的多功能自省和动态运行时修改功能使得在函数 locals 中更改 x 的类型成为可能。
    • 它可能有一个非关联的__mul__ 方法,或者不接受RHS 上的int,就此而言。
    • @KarlKnechtel:当然-我会将第一个归类为“有副作用”,第二个与您是否可以优化 RHS 常量乘法无关-如果语法允许,您仍然可以这样做在编译器中并在 __mul__ 方法中正确失败
    • 我想开玩笑说 x * 10 * 10 == x * 101 对于 10 的大值。这个答案表明这个笑话有一些价值,因为它说明了,据我们所知,x .__mul__ 可以实现区间算术。 (我已经重写了这条评论,以消除“字面上”这个词,作为对 Blindy 的让步。)
    【解决方案2】:

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

    试试:

    def f2(x):
        10*10*x
    

    这应该被优化。

    【讨论】:

    • 酷!也在 cpython 2.6.6 上确认
    猜你喜欢
    • 2016-08-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-18
    • 2020-11-20
    相关资源
    最近更新 更多