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

时间:2025-01-23 18:34:56

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


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

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

 import dis

 def count():
fs = []
for i in range(1,4):
def f():
return i*i
fs.append(f)
return fs def run():
f1, f2, f3 = count()
# When the function called, the value of i is 3
print(f1())
print(f2())
print(f3()) # dis.dis(count)
run()

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

   5           0 BUILD_LIST               0
3 STORE_FAST 0 (fs) 6 6 SETUP_LOOP 54 (to 63)
9 LOAD_GLOBAL 0 (range)
12 LOAD_CONST 1 (1)
15 LOAD_CONST 2 (4)
18 CALL_FUNCTION 2 (2 positional, 0 keyword pair)
21 GET_ITER
>> 22 FOR_ITER 37 (to 62)
25 STORE_DEREF 0 (i) 7 28 LOAD_CLOSURE 0 (i)
31 BUILD_TUPLE 1
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>)
37 LOAD_CONST 4 ('count.<locals>.f')
40 MAKE_CLOSURE 0
43 STORE_FAST 1 (f) 9 46 LOAD_FAST 0 (fs)
49 LOAD_ATTR 1 (append)
52 LOAD_FAST 1 (f)
55 CALL_FUNCTION 1 (1 positional, 0 keyword pair)
58 POP_TOP
59 JUMP_ABSOLUTE 22
>> 62 POP_BLOCK 10 >> 63 LOAD_FAST 0 (fs)
66 RETURN_VALUE

通过字节码可以看到,

第 10 行进入迭代,

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

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

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

相关阅读


1. 闭包函数