作用域与带有循环变量的默认参数相比较问题
程序员文章站
2022-07-10 11:41:37
这个问题是来自于美国作家 鲁特兹(Mark Lutz)所作的一本《Learning Python,Fourth Edition》书里提及到的。 原文翻译如下: 如果 lambda 或者 def 在函数中定义,嵌套在一个循环之中,并且嵌套的函数引用了一个上层作用域的变量,该变量被循环变量所改变,所有在 ......
这个问题是来自于美国作家 鲁特兹(Mark Lutz)所作的一本《Learning Python,Fourth Edition》书里提及到的。
原文翻译如下:
如果 lambda 或者 def 在函数中定义,嵌套在一个循环之中,并且嵌套的函数引用了一个上层作用域的变量,该变量被循环变量所改变,所有在这个循环变量中产生的函数将会有相同的值——在最后一次循环中完成时被引用变量的值。
我们来看下一个例子:
>>> def makeActions(): ... acts = [] ... for i in range(5): ... acts.append(lambda x: i ** x) ... return acts ... >>> acts = makeActions() >>> acts[0](2) 16 >>> acts[1](2) 16 >>> acts[2](2) 16 >>> acts[3](2) 16 >>> acts[4](2) 16
批注:这个例子试图在创建一个函数的列表,其中每个函数都记住嵌套作用域中当前变量 i 的值。但是这并不怎么有效:因为嵌套作用域中的变量在嵌套的函数被调用时才进行查找,所以它们实际上记住的是同样的值(在最后一次循环迭代中循环变量的值)。
我们来看下另一个例子:
>>> def makeActions(): ... acts = [] ... for i in range(5): ... acts.append(lambda x, i = i: i ** x) ... return acts ... >>> acts = makeActions() >>> acts[0](2) 0 >>> acts[1](2) 1 >>> acts[2](2) 4 >>> acts[3](2) 9 >>> acts[4](2) 16
批注:这个是在嵌套作用域的值和默认参数方面遗留的一种仍需要解释清楚的情况,而不是引用所在的嵌套作用域的值。为了实现你想要的效果,则必须使用默认参数把当前的值传递给嵌套作用域的变量。因为默认参数是在嵌套函数所创建时评估的(而不是在其稍后调用时),每一个函数记住了自己的变量 i 的值。
你也可以将上述代码所定义的函数等价于:
>>> def makeActions(): ... acts = [] ... for i in range(5): ... def func(x): ... return i ** x ... acts.append(func(x)) ... return acts ...
与
>>> def makeActions(): ... acts = [] ... for i in range(5): ... def func(x, i = i): ... return i ** x ... acts.append(func(x, i)) ... return acts ...
批注:这是一种相当隐晦的情况,但是它会在实际情况中发生,特别是在生成应用于 GUI 一些部件的回调处理函数代码中(例如,按钮的事件处理)。