如何在Python闭包中捕获值而非引用,解决循环内变量捕获异常?
如何在Python闭包中捕获值(而非引用)?
问题原因
Python的闭包采用延迟绑定规则:闭包内部引用的变量,会在函数实际被调用时才进行取值,而非闭包定义时就固定下来。
你给出的示例中,所有echo函数都引用了同一个循环变量str,循环结束后str的值已经被更新为列表最后一个元素zebra,所以所有函数调用时的返回值都为zebra。
注意:示例中使用了
list、str作为变量名,这两个名称属于Python内置类型标识,不建议用作自定义变量,避免覆盖内置功能。
解决方案
下面是三种常用的实现方式,都可以达到预期输出:
方法1:利用函数默认参数的提前求值特性
Python函数的默认参数会在函数定义阶段完成求值,我们可以借助这个特性绑定当前循环的变量值:
def makeListOfFuncs(strings): func_list = [] for s in strings: # 把当前循环的s赋值给默认参数current_str,定义阶段就固定值 def echo(current_str=s): return current_str func_list.append(echo) return func_list
方法2:使用functools.partial提前绑定参数
functools.partial可以为目标函数提前绑定参数,生成一个新的可调用对象,参数值在绑定阶段就会固定:
from functools import partial def makeListOfFuncs(strings): func_list = [] for s in strings: def echo(current_str): return current_str # 把当前s绑定为echo的入参 func_list.append(partial(echo, s)) return func_list
方法3:用立即执行的lambda创建临时作用域
通过外层lambda立刻执行传入当前循环值,生成临时作用域存储当前值,内层闭包捕获临时作用域的变量:
def makeListOfFuncs(strings): func_list = [] for s in strings: # 外层lambda接收s为参数并立刻调用,内层lambda捕获固定的x值 func_list.append((lambda x: lambda: x)(s)) return func_list
以上三种方法执行测试代码后,都会输出你预期的结果:
bird fish zebra
内容的提问来源于stack exchange,提问作者Sean B. Durkin
相关产品推荐
相关产品推荐

