闭包的概念与作用
发布日期:2021-09-14 03:23:56 浏览次数:3 分类:技术文章

本文共 3608 字,大约阅读时间需要 12 分钟。

什么是闭包(closure)

首先是维基百科中关于闭包的概念:

在一些语言中,在函数中可以(嵌套)定义另一个函数时,如果内部的函数引用了外部的函数的变量,则可能产生闭包。闭包可以用来在一个函数与一组“私有”变量之间创建关联关系。在给定函数被多次调用的过程中,这些私有变量能够保持其持久性。

两种解释

1.在一个外函数中定义了一个内函数,内函数里运用了外函数的临时变量,并且外函数的返回值是内函数的引用。这样就构成了一个闭包。

一般情况下,在我们认知当中,如果一个函数结束,函数的内部所有东西都会释放掉,还给内存,局部变量都会消失。但是闭包是一种特殊情况,如果外函数在结束的时候发现有自己的临时变量将来会在内部函数中用到,就把这个临时变量绑定给了内部函数,然后自己再结束。

2.望文知意,可以形象的把它理解为一个封闭的包裹,这个包裹就是一个函数,包裹里面的东西就是自由变量,自由变量可以在随着包裹到处游荡。一个闭包就是你调用了一个函数A,这个函数A返回了一个函数B给你,而且函数B引用了一个A中的变量。这个返回的函数B就叫做闭包。你在调用函数A的时候传递的参数就是自由变量。

根据以上,其实我们自己就可以总结出在python语言中形成闭包的三个条件,缺一不可:

  • 必须有一个内嵌函数(函数里定义的函数)——这对应函数之间的嵌套
  • 内嵌函数必须引用一个定义在闭合范围内(外部函数里)的变量——内部函数引用外部变量
  • 外部函数必须返回内嵌函数——必须返回那个内部函数

创建一个最简单的闭包

```def OutFun():   #外部函数    x=100       #自由变量    def InnerFun(y):    #内部函数        nonlocal x      #非局部变量        x+=y        print(x)	    return InnerFun     #返回内部函数引用t1=OutFun()     t1(1)```

####1. 外函数返回内部函数的引用 ####

t1=OutFun()将OutFun()函数的返回值(即InnerFun的引用)赋值给t1,那么t1就是内部函数InnerFun()的引用,那么什么是引用呢?在python中一切都是对象,比如a=1,这其实是在内存中开辟了一块空间来存储数字1,而a
这个变量名就存储了1所在内存的位置引用。这里的引用就像是C语言里面的指针,大家可以把引用理解成存储地址,a只是一个变量名,a里面存储的就是1这个数值所在的内存位置,就是a里面存储了1的引用。同样的函数的引用亦是如此!函数定义的时候就是在内存里面开辟一片空间来存储函数内部代码以及内部变量等。InnerFun只不过是一个变量名字,而其里面却存储了对InnerFun()这个函数所在位置的引用,你也可以这样操作b=InnerFun,那么就把函数的所在位置也赋值给了b,那么b()和InnerFun()是相等身份的。

2.外部函数把临时变量绑定给内函数

在正常情况下,当一个函数执行结束后,会把自己的临时变量释放给内存,之后这些临时变量都不会存在了。但是在闭包中却不是这样的,外部函数发现自己的临时变量会在将来的内部函数中使用,于是在自己结束生命周期的时候,返回内部函数的同时,会把在内部函数中用到的临时变量绑定到内部函数中,因此即使外部函数结束了,它内部的临时变量还是依然可以在内部函数中使用的,这就是原因所在。这一点需要我们好好理解到位!

内部函数修改外部函数的临时变量

我们可以将外部函数的临时变量绑定到内部函数中,保证了在外部函数结束生命周期的时候不会让临时变量释放掉,那么怎么在内部函数中修改绑定了的外部函数的临时变量呢?

在python基础语法中,我们为了在函数内部修改一个全局变量,一般有两种方法:1.在函数内部使用global声明变量为全局变量,即可修改。2.将全局变量的类型转换成可变类型,如:列表,就可以实现在函数内部进行修改
那么在闭包里面其实也有两种方法可以满足我们的要求:1.使用nonlocal声明临时变量,将其变为全局变量。2.将外部函数的临时变量定义为可变数据类型的变量,这样就可以直接在内部函数中修改了。我们在上面的那个程序就是演示了方法1的使用。

```def OutFun():   #外部函数    x=100       #自由变量    def InnerFun(y):    #内部函数        nonlocal x      #非局部变量        x+=y        print(x)	    return InnerFun     #返回内部函数引用t1=OutFun()     t1(1)```

闭包的作用

以下介绍一下闭包的主要作用,其他的作用请在开发过程中多多体会!

作用:

闭包可以让临时变量在外部函数生命周期结束时,仍然存在于内存之中!

例1

比如说,如果你希望函数的每次执行结果,都是基于这个函数上次的运行结果。我以一个类似棋盘游戏的例子来说明。假设棋盘大小为50*50,左上角为坐标系原点(0,0),我需要一个函数,接收2个参数,分别为方向(direction),步长(step),该函数控制棋子的运动。棋子运动的新的坐标除了依赖于方向和步长以外,当然还要根据原来所处的坐标点,用闭包就可以保持住这个棋子原来所处的坐标。

```origin = [0, 0] legal_x = [0, 50]  legal_y = [0, 50] def create(pos=origin):       def player(direction,step):                   new_x = pos[0] + direction[0]*step            new_y = pos[1] + direction[1]*step            pos[0] = new_x            pos[1] = new_y            #注意!此处不能写成 pos = [new_x, new_y],因为参数变量不能被修改,而pos[]是容器类的解决方法         return pos       return player    player = create() # 创建棋子player,起点为原点  print player([1,0],10) # 向x轴正方向移动10步  print player([0,1],20) # 向y轴正方向移动20步  print player([-1,0],10) # 向x轴负方向移动10步 ```

那么大家猜猜输出结果会是什么呢?

输出为:

``` [10, 0]  [10, 20]   [0, 20] ```

观察结果可以发现,每一次移动棋子,都是在上一次移动后为基础来移动的,除非重启程序后,pos才会为[0,0],此时才是真正的原点位置。

例2

我们首先来创建一个闭包:

```def funx():	x=5	def funy():		nonlocal x		x+=1		return x	return funy```

那么在交互环境下执行验证:

```>>> a=funx()>>> a()6>>> a()7>>> a()8>>> a()9```

可以发现,每一次输出的结果都是以上一次运行结果为基础的。这同时也验证了例1。

让我们来进一步看看,这个闭包到底是个什么东西,请看以下代码段:

```>>> a.__closure__(
,)>>> type(a.__closure__)
>>> type(a.__closure__[0])
>>> a.__closure__[0].cell_contents9>>> a()10>>> a.__closure__[0].cell_contents10>>> def test():pass>>> test.__closure__==NoneTrue>>> ```

这样我们就明白了,形成闭包之后,闭包函数会获得一个非空的__closure__属性(对比我们最后的函数test,test是一个不具备闭包的函数,它的__closure__属性是None),这个属性是一个元组。元组里面的对象为cell对象,而访问cell对象的cell_contents属性则可以得到闭包变量的当前值(即上一次调用之后的值)。而随着闭包的继续调用,变量会再次更新。所以可见,一旦形成闭包之后,python确实会将__closure__和闭包函数绑定作为储存闭包变量的场所。

转载地址:https://blog.csdn.net/weixin_40612082/article/details/80550557 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:python装饰器--你绝对能掌握!
下一篇:UnicodeDecodeError: 'ascii' codec can't decode byte 0xe8 in position...解决方法

发表评论

最新留言

表示我来过!
[***.240.166.169]2024年04月14日 01时06分44秒