Does python reuse repeated calculation results?
You can check that with dis.dis
. The output is:
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
So it won't cache the result of the expression in parentheses. Though for that specific case it would be possible, in general it is not, since custom classes can define __add__
(or any other binary operation) to modify themselves. For example:
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
If you have an expensive function of which you would like to cache the result, you can do so via functools.lru_cache
for example.
On the other hand, the compiler will perform constant folding as can be seen from the following examples:
>>> 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
EDIT: This answer applies only to the default CPython interpreter of the Python language. It may not be applicable to other Python implementations that adopts JIT compilation techniques or uses a restricted Python sublanguage that allows static type inference. See @Jörg W Mittag's answer for more details.
No it will not. You can do this to see the compiled code:
from dis import dis
dis("r=(x+y+z+1) + (x+y+z+2)")
Output:
0 LOAD_NAME 0 (x)
2 LOAD_NAME 1 (y)
4 BINARY_ADD
6 LOAD_NAME 2 (z)
8 BINARY_ADD
10 LOAD_CONST 0 (1)
12 BINARY_ADD
14 LOAD_NAME 0 (x)
16 LOAD_NAME 1 (y)
18 BINARY_ADD
20 LOAD_NAME 2 (z)
22 BINARY_ADD
24 LOAD_CONST 1 (2)
26 BINARY_ADD
28 BINARY_ADD
30 STORE_NAME 3 (r)
32 LOAD_CONST 2 (None)
34 RETURN_VALUE
This is partially because Python is dynamically-typed. So the types of variables are not easily known at compile time. And the compiler has no way to know whether the +
operator, which can be overloaded by user classes, could have any side effect at all. Consider the following simple example:
class A:
def __init__(self, v):
self.value = v
def __add__(self, b):
print(b)
return self.value + b
x = A(3)
y = 4
r = (x + y + 1) + (x + y + 2)
For simple expressions, you can just save the intermediate results to a new variable:
z = x + y + 1
r = z + (z + 1)
For functions calls, functools.lru_cache
is another option, as already indicated by other answers.