Python_Tips[2] -> 函数延后估值及字节码分析

函数延后估值及字节码分析


在一个循环中定义了函数 f 但是并未对其进行调用,在循环结束后调用,此时i值为3故最终3个函数输出均为9。而非1, 4, 9。

这是由于在定义闭包函数 f 时,传入变量 i,而在循环结束后才调用函数,此时的 i 已为 3,下面使用字节码来查看并论证这一运行顺序。

 1 import dis
 2 
 3 def count():
 4     fs = []
 5     for i in range(1,4):
 6         def f():
 7             return i*i
 8         fs.append(f)
 9     return fs
10 
11 def run():
12     f1, f2, f3 = count()
13     # When the function called, the value of i is 3
14     print(f1())
15     print(f2())
16     print(f3())
17 
18 # dis.dis(count)
19 run()

使用 dis对count函数的字节码进行查看,得到解释器运行字节码如下

 1   5           0 BUILD_LIST               0
 2               3 STORE_FAST               0 (fs)
 3 
 4   6           6 SETUP_LOOP              54 (to 63)
 5               9 LOAD_GLOBAL              0 (range)
 6              12 LOAD_CONST               1 (1)
 7              15 LOAD_CONST               2 (4)
 8              18 CALL_FUNCTION            2 (2 positional, 0 keyword pair)
 9              21 GET_ITER
10         >>   22 FOR_ITER                37 (to 62)
11              25 STORE_DEREF              0 (i)
12 
13   7          28 LOAD_CLOSURE             0 (i)
14              31 BUILD_TUPLE              1
15              34 LOAD_CONST               3 (<code object f at 0x0000000000547AE0, file "C:/Users/XXXXX/Documents/Python Note/10_Python_Tips/10.4_Method_Call/Method_Call.py", line 7>)
16              37 LOAD_CONST               4 ('count.<locals>.f')
17              40 MAKE_CLOSURE             0
18              43 STORE_FAST               1 (f)
19 
20   9          46 LOAD_FAST                0 (fs)
21              49 LOAD_ATTR                1 (append)
22              52 LOAD_FAST                1 (f)
23              55 CALL_FUNCTION            1 (1 positional, 0 keyword pair)
24              58 POP_TOP
25              59 JUMP_ABSOLUTE           22
26         >>   62 POP_BLOCK
27 
28  10     >>   63 LOAD_FAST                0 (fs)
29              66 RETURN_VALUE

通过字节码可以看到,

第 10 行进入迭代,

第 11 行中的 STORE_DEREF 会更新变量 i 的值,

第 13 行开始,进入到函数 f 定义的部分,而第 13 行则是关键,这里载入 Enclosure 即闭包上层的局部变量 i,而不是当前 i 的值,随后完成整个闭包函数 f 并返回。

因此,在 3 个函数 f 中,所存储的均为变量 i,所以在调用时结果自然相同。

相关阅读


1. 闭包函数

原文地址:https://www.cnblogs.com/stacklike/p/8227584.html