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)