问题描述:

使用如下代码块的写法,在循环体中使用普通函数进行循环计算,是无法获得预期结果的。

如下图案例中所示:
1、我想通过for循环获取0、1、2、3这四个值,然后在函数中进行运算(与参数a相加),并使用append方法将最终结果存入列表list1中。
2、理论上生成的结果应该是1、2、3、4,但实际生成的结果却是4、4、4、4。

list1 = []
for i in range(0,4):
    def function1(a):
        return i+a
    list1.append(function1)

for f in list1:
    print('这是list1中生成的结果:'+str(f(1)))

原因分析:

1、首先,函数只有被调用(写个f()就能调用)后才能生效、运行,并调用所需参数值,生成运算结果。

2、但在该代码块中,for循环内的函数并没有在每次循环时被立即执行(即调用),list1.append(function1),也就是说通过append方法附加到列表list1中的仅仅是函数function1本身,而非函数function1执行后的结果。

3、而在整个for循环结束之后,函数function1才在最后那两行代码中被执行了(print里的f()就是对函数进行调用)。

4、在最后两行代码执行function1这个函数时,需要获取并使用变量 i 的值,但for循环此时已经结束运行了,i的值也已经被一层层覆盖变成3了,
(根据for循环的运行原理可知,第二次循环时生成的i=1会覆盖上一次循环生成的i=0,依次类推,在循环结束之后i的最终值就是3。)

5、而普通函数又无法保存每次循环时变量i的值,
因此等到循环结束调用函数f(1)、将参数a=1传递给函数进行 i+a 的运算时,for循环中生成的所有函数都只能使用最后生成的那个变量i的值“3”,
因此输出并附加到列表list1中的结果全部都是3+1,也就是4个4。


解决方案:

有两种解决方案:

1、第一种方法:
在for循环内直接调用函数function1(写个function1()),然后将函数生成的结果(而非函数)附加到列表中,不要在for循环外打印时才调用函数。

list1 = []
for i in range(0,4):
    def function1():
        return i
    list1.append(function1())

for i1 in list1:
    print('这是list1中生成的结果:'+str(i1))

2、第2种方法:
使用闭包函数,将每次循环所得变量i的值,
保存到每次循环生成的闭包函数的__closure__属性中。

list2 = []
for i in range(0,4):
    def function2(i):
        def f_closure():
            return i
        return f_closure
    list2.append(function2(i))

for i2 in list2:
    print('这是list2中生成的结果:'+str(i2()))