There are several ways to optimize this code:
It is faster to using itertools.repeat(None, times)
to control the number of loops (this avoids creating new, unused integer objects on every iteration).
You can gain speed by putting this in a function or generator (local variables are faster than global variables.
You can gain speed by saving the intermediate results in a variable, avoiding the [-1]
indexed lookup (LOAD_FAST / STORE_FAST is quicker than LOAD_CONST -1 and BINARY_SUBSCR).
You can improve speed by using a pre-bound method instead of y.append
.
For example:
from itertools import repeat
def nest(func, x, times):
result = [x]
result_append = result.append
for _ in repeat(None, times):
x = func(x)
result_append(x)
return result
Here is a sample call:
>>> def double(x):
return 2 * x
>>> nest(double, 3, 5)
[3, 6, 12, 24, 48, 96]
Here is the disassembly showing the tight inner-loop, use of local variables, and the bound method:
>>> from dis import dis
>>> dis(nest)
2 0 LOAD_FAST 1 (x)
3 BUILD_LIST 1
6 STORE_FAST 3 (result)
3 9 LOAD_FAST 3 (result)
12 LOAD_ATTR 0 (append)
15 STORE_FAST 4 (result_append)
4 18 SETUP_LOOP 45 (to 66)
21 LOAD_GLOBAL 1 (repeat)
24 LOAD_CONST 0 (None)
27 LOAD_FAST 2 (times)
30 CALL_FUNCTION 2
33 GET_ITER
>> 34 FOR_ITER 28 (to 65)
37 STORE_FAST 5 (_)
5 40 LOAD_FAST 0 (func)
43 LOAD_FAST 1 (x)
46 CALL_FUNCTION 1
49 STORE_FAST 1 (x)
6 52 LOAD_FAST 4 (result_append)
55 LOAD_FAST 1 (x)
58 CALL_FUNCTION 1
61 POP_TOP
62 JUMP_ABSOLUTE 34
>> 65 POP_BLOCK
7 >> 66 LOAD_FAST 3 (result)
69 RETURN_VALUE