Python闭包中函数调用实参与形参的匹配机制疑问
首先你要先抓一个最核心的事实:你调用outer_func(i)拿到的返回值,就是内部定义的inner_func这个函数本身,它不是什么特殊的“闭包实例”,就是个普通的Python函数对象,只是额外带了对外层作用域变量的引用而已。
为什么调用closure传的参数不会覆盖x?
Python里每个函数的形参列表、作用域边界,是在def语句定义函数的时候就完全固定死的,不同函数的形参属于完全独立的作用域,根本不会互相串。outer_func定义时写的形参是x,这个x只属于outer_func运行时的局部作用域;inner_func定义时写的形参是z,这个z只属于inner_func自己的局部作用域。你最后拿到的closure本质就是inner_func,调用它传参的时候,只会给inner_func自己的形参赋值,半分碰不到外层的x,自然不存在覆盖的可能。
z是怎么被赋值的?Python怎么知道实参要传给z?
这个逻辑没有任何魔法,规则非常直白:你调用哪个函数,就严格按照哪个函数定义时写的形参列表匹配实参。
当初定义inner_func的时候,括号里明明白白只写了z一个形参,那之后不管你把这个函数对象赋值给叫closure还是别的名字的变量,只要调用它,传入的第一个位置参数就会绑定给z,不需要什么特殊判定。
闭包里的x和y是怎么来的?
正常来说outer_func执行完之后,它内部的局部变量x、y本该被回收销毁,但因为内部的inner_func引用了这两个变量,Python就会把这两个变量的绑定关系打包,附加到inner_func的函数对象上,跟着函数一起返回。这部分闭包存储和inner_func自己的形参存储是完全隔离的两块空间,互不干扰。
我们拿第一轮循环的执行流程拆解一遍,逻辑就完全顺了:
- i=0时调用
outer_func(0):- 先给
outer_func的形参x绑定值0 - 在
outer_func的局部作用域里创建变量y,赋值为4 - 定义内部函数
inner_func,此时这个函数会自动记住当前外层作用域里的x=0、y=4 - 把创建好的
inner_func函数对象返回,赋值给变量closure
- 先给
- 调用
closure(5)(也就是i+5=0+5=5):- 识别到
closure就是刚才创建的inner_func,直接执行它的内部代码 - 按
inner_func自己的形参规则,把传入的5绑定给形参z - 执行打印和计算时,x和y从函数携带的闭包记录里取值(0和4),z用刚绑定的5,最终打印
x = 0, y = 4, z = 5,返回计算结果9
- 识别到
- 后续i=1、i=2的轮次逻辑完全一致,只是每次调用
outer_func都会生成一个全新的局部作用域,返回的inner_func携带的x值分别是1、2,互不影响。
你要是想验证这个逻辑,可以自己加两行代码打印属性看:
closure = outer_func(2) # 打印函数自身的形参和局部变量 print(closure.__code__.co_varnames) # 输出 ('z',) # 打印闭包携带的外层变量值 print([cell.cell_contents for cell in closure.__closure__]) # 输出 [2, 4]
可以很直观看到,形参z和闭包里存的x、y是完全分开的。
内容的提问来源于stack exchange,提问作者Mr.Lupine

