Python 闭包的高级用法详解

    所谓闭包,就是指内函数使用了外函数的局部变量,并且外函数把内函数返回出来的过程,这个内函数称之为闭包函数。可以理解为是函数式编程中的封装。



    内部函数可以使用外部函数定义的属性:外部函数调用后,返回内部函数的地址,完成内部函数的调用。

在这里插入图片描述



def outer():
    num = 100

    def inner():
        return num

    return inner

result = outer()
print(f'外部函数调用后,返回内部函数的地址:{result}')
print('通过内部函数的地址,完成内部函数的调用,使用外部函数定义的属性值为:', result())


    一般写法和闭包写法的对比:



    一般写法,在 传入的 x 和 y 不变情况下,每次调用函数时,都要重复传入相同的 x、y 参数。



在这里插入图片描述



def cal(x,y,z):
    print(x+y+z)

cal(2,3,4)
cal(2,3,5)
cal(2,3,6)
cal(2,3,7)
cal(2,3,8)


    闭包写法,在x、y固定的情况下,只需要通过调用内部函数去传 z参数就可以了。如果要计算的结果太多,要传的 z 可能好多,也可以通过循环去实现传参,减少重复调用函数。



在这里插入图片描述



def cal(x,y):
    def cal_in(z):
        print(x+y+z)

    return cal_in

result = cal(2,3)
result(4)
result(5)
result(6)
result(7)
result(8)


    全局变量对闭包函数调用时所引用的变量究竟有没有影响?



    可见,当调用闭包函数时,是直接调用了外部函数变量
n = 3, 而不是全局变量 n = 4, 因此结果为 27。也证明了执行函数时,它的优先级是先搜索内部作用域的变量,全局变量对闭包函数的执行并没有影响。



在这里插入图片描述



def pow1():
    n = 3

    def pow2(x):
        return x ** n

    return pow2

n = 4

result = pow1()
print(result(3))


    closure是内部函数的一个属性,用来保存环境变量,返回的是一个元组,每一项都是闭包函数引用的外部变量。可以通过cell_contents 将被引用的变量打印出来。



    首先打印 result 的类型,可见是一个函数类型,证明在这个函数里面,我们可以将另外一个函数作为这个函数的返回值去进行返回的。 打印 closure, 得出内部函数的属性所对应的环境变量,可见它是一个元组类型。最后通过 cell_contents去获取变量的值。要注意的是,由于环境变量是一个元组,cell_contents是没有元组直接获取变量值的属性,因此必须先通过索引去获取你要的数据,否则会有报错。



在这里插入图片描述



    假设把 n = 3 放到最外层,变成全局变量,结果会一样吗?



    可见结果已经有刚才的 27 变成了 81。由于 n = 3 已经由局部变量变成了全局变量,当调用 pow2(x)时,并没有引用 pow1() 的变量,因此它并不是闭包函数,只是一个普通的嵌套函数,当pow2(x)搜索内部作用域没找到 n 的变量,就会搜索全局变量,由于全局变量同时存在 n = 3 和 n = 4, 这时变量就相当于 n = 4, 结果就是 3的4次方为81,而不是刚才执行闭包函数时的27了。



在这里插入图片描述



    同样,根据刚才介绍的 closure 概念,我们可以通过打印 closure 去验证是否闭包。



    可见结果为 None, 证明这不是闭包函数。



在这里插入图片描述



    当 n 未定义的情况下出现的报错:



在这里插入图片描述



def pow1():
    n = 3

    def pow2(x):
        n +=1
        return x ** n

    return pow2


result = pow1()
print(result(3))


    解决报错的方法:可以用 nonlocal 关键字声明 一个变量, 表示这个变量不是局部变量空间的变量,需要向上一层变量空间找这个变量。



    可见添加 nonlocal后,闭包函数可以正常调用 n = 3 并执行累加。因此当调用第一次时,结果为 81, 调用第二次时,结果为 243, n 相当于变成了5



在这里插入图片描述



    再看另外一个循环的例子,循环中变量分别是1、2,但实际最终返回的结果为4,4。 原因是返回的函数引用了变量i,但它并非立刻执行。等到2个函数都返回时,它们所引用的变量i已经变成了2。从打印 cell_contents 结果可见。

在这里插入图片描述



def pow1():
    n=2
    L = []
    for i in range(1,3):
        def pow2():
            return i ** n
        L.append(pow2)
    return L

f1,f2 = pow1()
print(f1())
print(f2())
print(f1.__closure__)
print(f2.__closure__)
print(f1.__closure__[0].cell_contents)
print(f2.__closure__[0].cell_contents)


    要改变分别执行变量1、2, 让pow2() 形成闭包。可见被引用的变量也分别是1,2,返回的结果就是我们预期的1,4



在这里插入图片描述



def pow1():
    n=2
    L = []
    for i in range(1,3):
        def extra(i):
          def pow2():
              return i ** n
          return pow2
        L.append(extra(i))
    return L


f1,f2 = pow1()
print(f1())
print(f2())
print(f1.__closure__)
print(f2.__closure__)
print(f1.__closure__[0].cell_contents)
print(f2.__closure__[0].cell_contents)


    关于更多闭包的高级用法疑问,欢迎留言!

  • 19
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值