我们知道,在Python中方法内部的局部变量都保存在栈帧中,一旦方法结束栈帧被销毁,则局部变量也一并被销毁。有如下Python程序

1
2
3
4
5
6
7
8
9
10
def outer():
x = 2333
y = 666

def inner():
return x + y

return inner

func = outer()

在outer方法被调用时,outer所对应的栈帧在栈上被创建,局部变量x和y的值被保存在栈帧中。当outer方法执行完毕之后,outer的栈帧被销毁,outer栈帧中的局部变量的值也会被销毁。此时局部变量被保存在哪里才能使得接下来函数func被调用时能得到正确的x和y的值呢?

使用如下代码可以打印outer函数和func函数的字节码

1
2
3
4
5
6
import dis

print '>>>>>>>>>>>>>> outer <<<<<<<<<<<<<<<<'
dis.dis(outer)
print '>>>>>>>>>>>>>> func <<<<<<<<<<<<<<<<<'
dis.dis(func)

在CPython2.7中运行以上程序,得到结果如下

>>>>>>>>>>>>>> outer <<<<<<<<<<<<<<<<
2           0 LOAD_CONST                1 (2333)
            3 STORE_DEREF               0 (x)

3           6 LOAD_CONST                2 (666)
            9 STORE_DEREF               1 (y)

5           12 LOAD_CLOSURE             0 (x)
            15 LOAD_CLOSURE             1 (y)
            18 BUILD_TUPLE              2
            21 LOAD_CONST               3 (<code object inner at 0x105100e30, file "", line 5>)
            24 MAKE_CLOSURE             0
            27 STORE_FAST               0 (inner)

8           30 LOAD_FAST                0 (inner)
            33 RETURN_VALUE        
>>>>>>>>>>>>>> func <<<<<<<<<<<<<<<<<
6           0 LOAD_DEREF                0 (x)
            3 LOAD_DEREF                1 (y)
            6 BINARY_ADD          
            7 RETURN_VALUE        

上面我们需要重点观察的就是 STORE_DEREFLOAD_DEREF 指令

指令 功能
STORE_DEREF 把栈顶元素保存到函数对象的 __closure__ 属性的指定下标中
LOAD_DEREF 根据 index 从函数的 __closure__ 属性中取得变量

由字节码我们知道Python中闭包的变量是被保存在函数对象中的,我们也可以利用如下的代码获取一个函数对象中的所有的闭包变量:

1
2
for param in func.__closure__:
print param.cell_contents

打印结果为

2333
666

参考:
Python闭包研究
Where does Python store the name binding of function closure?
Disassembler for Python bytecode