关于闭包以及其常见问题的理解——基于python

函数作为返回值

在 python 中,返回值为一个函数名的函数被称为 高阶函数
而所谓返回一个函数,一般情况下即意味着:执行这个函数并输出相应结果:

# 定义一个返回值为函数的函数
def myF1():    
    def myF2():
        print("In myF2")
        return 2
    return myF2

# 使用上面定义
# 调用myF1, 返回一个函数myF2,赋值给f2
f2 = myF1()
f2()

上述代码的执行结果为:

In myF2
2

其实质是:调用了 myF1, 返回一个函数 myF2,赋值给了 f2,则 f2 的执行结果即为 myF2() 函数的执行结果

复杂一点的高阶函数

上述例子中,作为返回值的函数并没有和其所处于的外部函数有过多的关联。

当作为返回值的函数使用了其所处的外部函数的参数及局部变量时,即出现了闭包:

# args:参数列表
# 1 myF4定义函数,返回内部定义的函数myF5
# 2. myF5使用了外部变量,这个变量是myF4的参数

def myF4( *args):
    def myF5():
        rst = 0
        for n in args:
            rst += n
        return rst
    return myF5

f5 = myF4(1,2,3,4,5,6,7,8,9,0)
f5()

调用 f5,结果为:

45

闭包:

定义:

一个函数在其内部定义函数,并且内部的函数使用了外部函数的参数或者局部变量,当内部函数被作为返回值的时候,相关的参数和变量将保存在作为返回值的函数中,这种结构,叫做闭包。

闭包结构有以下特性:

  1. 返回的函数并不会立刻执行,它会保留所使用的相关的参数和变量,在调用的时候才会被执行
  2. 所谓 “相关的参数和变量将保存在作为返回值的函数中” ,其实际是传址操作而非传值,即保存在函数里的参数的值并不是不会改变的

    这些特性会在典型的闭包会遇到的坑中体现:
    来看案例:

def count():
    # 定义列表,列表里存放的是定义的函数
    fs = []
    for i in range(1,4):
        # 定义了一个函数f
        # f是一个闭包结构
        def f():
            return i*i
        fs.append(f)
    return fs

f1,f2,f3 = count()
print(f1())
print(f2())
print(f3())

上述例子中,我们定义了一个 count 函数,其中的 for 循环将执行三次,每次调用都会将一个 f(i) 函数放入 fs 这个列表中作为新的一项。
因为 fs[] 是一个以函数作为元素的列表, 所以这里构成闭包的还是最外层返回 fs[] 的 count 函数。

接下来的赋值语句。使用了可迭代数据的特性,将 count() 函数调用后得到的列表分拆为三个元素,分别赋值给 f1 , f2 , f3 。则此三个变量实际为函数变量

按照一般的理解,三次循环时的 i 值分别为 1,2,3 。则上述代码的执行结果应该为 1 4 9

但实际上…… 都说了有坑那肯定不是以为的那样。
实际结果:
这里写图片描述

原因在于上述的第二点特性。

作为返回值的函数 (fs 列表中的元素)使用了循环变量 i,因为返回的函数不会立即执行,因此,当三个函数都被返回时,i 的值已经变成了 3,因此在调用的时候,i 已经是 3。

此问题描述为:

返回闭包时,返回函数不能引用任何循环变量

注意哦,这里的 “f1,f2,f3 = count()” 与该特性无关,只是一个普通的赋值而已。

关于保存在各个返回函数中的参数 i 的值的变化过程可参考下图:
(自己拿 ppt 画的,凑合看,谁没有个第一次。。)
这里写图片描述
可以看到,每一次循环之后,可以看做是生成了一个偏函数(即参数固定的函数),而这个参数 i 的值一直在变化,到了执行的时候,就已经是 3 了。

其解决办法是,再创建一个函数,用该函数的参数绑定循环变量当前的值,无论该循环变量后续如何更改,已绑定到函数参数的值不变,即 将传址变成传值,切断函数与会变化的循环参数 i 的关联。

# 修改上述函数
def count2():
    # 在外面再套一层 
    def f(j):
        def g():
            return j*j
        return g
    fs = []
    for i in range(1,4):
        fs.append(f(i))
    return fs

f1,f2,f3 = count2()
print(f1())
print(f2())
print(f3())

其执行结果:

1
4
9
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值