闭包
e.g.
1 2 3 4 5 6 7 8 |
def add(x): def do_add(value): return x + value return do_add add_5 = add(5) print add_5(1) # 6 print add_5(2) # 7 |
需要回答, 什么是闭包, CPython底层是如何实现的?
PyCodeObject
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
typedef struct { PyObject_HEAD int co_argcount; /* #arguments, except *args */ int co_nlocals; /* #local variables */ int co_stacksize; /* #entries needed for evaluation stack */ int co_flags; /* CO_..., see below */ PyObject *co_code; /* instruction opcodes */ PyObject *co_consts; /* list (constants used) */ PyObject *co_names; /* list of strings (names used) */ PyObject *co_varnames; /* tuple of strings (local variable names) */ // 保存使用了的外层作用域中的变量名集合 (编译时就知道的! 被嵌套的时候有用) PyObject *co_freevars; /* tuple of strings (free variable names) */ // 保存嵌套作用域中使用的变量名集合, (编译时就知道的! 包含嵌套函数时有用) PyObject *co_cellvars; /* tuple of strings (cell variable names) */ /* The rest doesn't count for hash/cmp */ PyObject *co_filename; /* string (where it was loaded from) */ PyObject *co_name; /* string (name, for reference) */ int co_firstlineno; /* first source line number */ PyObject *co_lnotab; /* string (encoding addrlineno mapping) See Objects/lnotab_notes.txt for details. */ void *co_zombieframe; /* for optimization only (see frameobject.c) */ PyObject *co_weakreflist; /* to support weakrefs to code objects */ } PyCodeObject; |
我们关注两个, co_freevars
和 co_cellvars
1 2 3 |
co_freevars, 保存使用了的外层作用域中的变量名集合 (编译时就知道的! 被嵌套的时候有用) co_cellvars, 保存嵌套作用域中使用的变量名集合, (编译时就知道的! 包含嵌套函数时有用) |
对于我们上面的那个示例, add
是外层函数, do_add
是嵌套函数, 我们可以通过func_code
打印看看
1 2 3 4 5 6 7 8 9 10 11 12 |
def add(x): # 外层函数 # 外层函数, 没有使用了外层作用域变量, 被嵌套函数使用了'x' print add.func_code.co_freevars # () print add.func_code.co_cellvars # ('x',) def do_add(value): # 嵌套函数 return x + value # 内层函数, 使用了外层作用域便令'x', 没有嵌套函数故嵌套作用域变量名集合空 print do_add.func_code.co_freevars # ('x',) print do_add.func_code.co_cellvars # () return do_add |
此时图示
这时候, 只是记录了使用到的变量名, 标记下是否使用了外层的/被内层使用的变量
具体的值是在运行时确定的, 例如
1 |
add(5) |
此时x=5
, 这个是在add
的名字空间里面的, 那么, x=5
是怎么传递到嵌套函数内? 嵌套函数又是如何知晓x
的值?
记住这两个问题, 然后我们首先来看一个新的数据结构