python是否会重复使用重复的计算结果?

use*_*076 27 python interpreter interpreted-language

如果我有一个希望在Python中求值的表达式,例如r下面的代码片段中的表达式,那么Python解释器会很聪明并且可以重用子结果x+y+z,还是只求它两次?我也想知道这个问题的答案是否与编译语言(例如C)相同。

x = 1
y = 2
z = 3
r = (x+y+z+1) + (x+y+z+2)
Run Code Online (Sandbox Code Playgroud)

有人提出这个问题与这个问题相似。我相信这是相似的。但是,我认为链接的问题不是“最小示例”。同样在链接的问题中,操作顺序没有歧义,例如,在与此问题类似的示例中,没有定义的操作顺序(在数学上),具体取决于各个函数调用的顺序(即模棱两可),可能会做得更好或更糟的优化工作。考虑(a b b a)(a b b a)(b a a * b),存在嵌套的重复子字符串,并且根据预处理的顺序和数量,可以执行许多不同的优化。

a_g*_*est 20

您可以使用进行检查dis.dis。输出为:

  2           0 LOAD_CONST               0 (1)
              2 STORE_NAME               0 (x)

  3           4 LOAD_CONST               1 (2)
              6 STORE_NAME               1 (y)

  4           8 LOAD_CONST               2 (3)
             10 STORE_NAME               2 (z)

  5          12 LOAD_NAME                0 (x)
             14 LOAD_NAME                1 (y)
             16 BINARY_ADD
             18 LOAD_NAME                2 (z)
             20 BINARY_ADD
             22 LOAD_CONST               0 (1)
             24 BINARY_ADD
             26 LOAD_NAME                0 (x)
             28 LOAD_NAME                1 (y)
             30 BINARY_ADD
             32 LOAD_NAME                2 (z)
             34 BINARY_ADD
             36 LOAD_CONST               1 (2)
             38 BINARY_ADD
             40 BINARY_ADD
             42 STORE_NAME               3 (r)
             44 LOAD_CONST               3 (None)
             46 RETURN_VALUE
Run Code Online (Sandbox Code Playgroud)

因此,它不会将表达式的结果缓存在括号中。尽管对于这种特定情况,通常是不可能的,因为自定义类可以定义__add__(或任何其他二进制操作)来修改自身。例如:

class Foo:
    def __init__(self, value):
        self.value = value

    def __add__(self, other):
        self.value += 1
        return self.value + other

x = Foo(1)
y = 2
z = 3
print(x + y + z + 1)  # prints 8
print(x + y + z + 1)  # prints 9
Run Code Online (Sandbox Code Playgroud)

如果您要缓存结果的功能很昂贵,则可以通过functools.lru_cache例如这样做。

另一方面,如以下示例所示,编译器将执行常量折叠

>>> import dis
>>> dis.dis("x = 'abc' * 5")
  1           0 LOAD_CONST               0 ('abcabcabcabcabc')
              2 STORE_NAME               0 (x)
              4 LOAD_CONST               1 (None)
              6 RETURN_VALUE
>>> dis.dis("x = 1 + 2 + 3 + 4")
  1           0 LOAD_CONST               0 (10)
              2 STORE_NAME               0 (x)
              4 LOAD_CONST               1 (None)
              6 RETURN_VALUE
Run Code Online (Sandbox Code Playgroud)