闭包:
在一个外函数中定义了一个内函数,内函数里运用了外函数的临时变量,并且外函数的返回值是内函数的引用。这样就构成了一个闭包。
1 外函数返回了内函数的引用:
当我们在python中定义一个函数def demo(): 的时候,内存当中会开辟一些空间,存下这个函数的代码、内部的局部变量等等。这个demo只不过是一个变量名字,它里面存了这个函数所在位置的引用而已。我们还可以进行x = demo, y = demo, 这样的操作就相当于,把demo里存的东西赋值给x和y,这样x 和y 都指向了demo函数所在的引用,在这之后我们可以用x() 或者 y() 来调用我们自己创建的demo() ,调用的实际上根本就是一个函数,x、y和demo三个变量名存了同一个函数的引用。
同时我们发现,一个函数,如果函数名后紧跟一对括号,相当于现在我就要调用这个函数,如果不跟括号,相当于只是一个函数的名字,里面存了函数所在位置的引用。
2 外函数把临时变量绑定给内函数:
一般情况下,在我们认知当中,如果一个函数结束,函数的内部所有东西都会释放掉,还给内存,局部变量都会消失。但是闭包是一种特殊情况,如果外函数在结束的时候发现有自己的临时变量将来会在内部函数中用到,就把这个临时变量绑定给了内部函数,然后自己再结束。
Python中一切都是对象,虽然函数我们只定义了一次,但是外函数在运行的时候,实际上是按照里面代码执行的,外函数里创建了一个函数,我们每次调用外函数,它都创建一个内函数,虽然代码一样,但是却创建了不同的对象,并且把每次传入的临时变量数值绑定给内函数,再把内函数引用返回。虽然内函数代码是一样的,但其实,我们每次调用外函数,都返回不同的实例对象的引用,他们的功能是一样的,但是它们实际上不是同一个函数对象。
Eg1
def outer(x): a = 10 def inner(y): nonlocal x x = y + x + a return x return inner print(outer(10)(3)) print(outer(20)(5)) print(outer(10)(3))
实行结果:
23
35
23
闭包中内函数修改外函数局部变量:
1 在python3中,可以用nonlocal 关键字声明 一个变量, 表示这个变量不是局部变量空间的变量,需要向上一层变量空间找这个变量。
2 在python2中,没有nonlocal这个关键字,我们可以把闭包变量改成可变类型数据进行修改,比如列表。
还有一点需要注意:使用闭包的过程中,一旦外函数被调用一次返回了内函数的引用,虽然每次调用内函数,是开启一个函数执行过后消亡,但是闭包变量实际上只有一份,每次开启内函数都在使用同一份闭包变量
Eg2
def outer(x): a = 10 def inner(y): nonlocal x x = y + x + a return x return inner a = outer(10) print(a(1)) print(a(3)) print(a(3))
实行结果:
21
34
47
注解:a = outer(10)这行代码是将外部函数的x赋值并绑定到内部函数中,并将内部函数的引用赋值给a
print(a(1))是直接调用内部函数,参数1是对应定义内部函数形参y的值
因为x是外部函数的形参,绑定到内部函数中,并且内部函数中使用了nonlocal,所以x是可以更改的,闭包函数每次调用都是使用同一份闭包变量,所以x的值不断增加(都是使用a进行调用闭包函数)。所以,a(3)和a(3)的结果不一样。Eg2中外部函数每次都会重新进行实例化,所以不会被重复调用