闭包详解
接下来我们看个例子理解一下:
# outer是外函数
def outer(i):
# j是外函数的局部变量
j = 1
# inner是内函数
def inner():
print(i + j)
return inner
f1 = outer(2)
>>> f1() # 调用之后会把i和j相加,打印3
3
f2 = outer(3)
>>> f2()
4
上面例子就是一个典型的闭包,首先理解一些基础概念
对象:
python中,一个变量叫对象,函数叫对象,类可以叫对象,总之一切都是对象。而在python中,无论你定义了一个变量、函数亦或是类,都会在内存中开辟一个空间保存你新创建的东西,当你在把它赋值给其他变量的时候,新的变量只是对其引用,并不会重新开辟新的空间,可以使用id(***)
函数来判断你对象的内存地址
引用:
在python中,外物皆对象,outer函数返回的是inner,也就是内函数的引用。抛开内外函数,假如你定义了两个函数,一个函数返回另一个函数,就是引用。你定义了a,b两个变量,a = b
就是a引用了b
闭包中修改变量
局部变量大部分情况在被调用完之后就会需要删除,而闭包中可以保留局部变量的这种特殊存在可以使我们保存局不信息
修改:
这里请注意,修改不是赋值,是直接改变了原变量的内存值,而赋值只是对原变量的引用
首先来看一个常规的修改变量例子:
def outer(i):
j = 1
def inner():
j += 1
print(i + j)
return inner
f1 = outer(2)
>>> f1()
---------------------------------------------------------------------------
UnboundLocalError Traceback (most recent call last)
<ipython-input-14-46be7c763a61> in <module>
9
10 f1 = outer(2)
---> 11 f1()
<ipython-input-14-46be7c763a61> in inner()
3
4 def inner():
----> 5 j += 1
6 print(i + j)
7 return inner
UnboundLocalError: local variable 'j' referenced before assignment
如上,会看到报变量“j"未定义就使用,这是因为内函数中无法修改外函数的变量,那要怎么在闭包中修改变量的值呢,有两种方法
第一种
用关键字nonlocal声明该变量,这样他的作用局域就不仅仅限制在内函数里面了,有点类似global的用法
def outer(i):
j = 1
def inner():
nonlocal j
j += 1
print(i + j)
return inner
f1 = outer(2)
>>> f1()
4
第二种
将局部变量放在可变类型中进行修改(列表、字典)
请注意,这种方法实际上是使用了新的内存来修改,并不是在原内存上面修改,但同样实现了修改的功能,可以同时保存局部变量和一个新的被修改过后的局部变量
def outer(i):
j = 1
def inner():
a = [j]
a[0] += 4
print(a[0])
print(j)
return inner
f1 = outer(2)
>>> f1()
5
1
可以看出当我们把 j 变量放在一个列表里面赋给a的时候,已经在新内存中重新开辟空间是一个新的对象,这时候可以对其进行任意的修改
闭包的作用
1、装饰器
装饰器在python中是一个非常重要的模块,后续会专门单独讲
2、面向对象
类对象的方法就有点类似于闭包的使用,在python中本身就是面向对象编程,所以我们感受不到,在其他语言比如JavaScript中,经常用闭包来实现面向对象编程
3、设计模式
最为常见的大概就是单例模式,这其实也是装饰器的应用, 设计模式 是开发人员在开发过程中面临一般问题的解决方案,是由众多开发人员经过很长时间的经验和错误总结出来的。
这也是作为一个资深的开发人员的必修课,后续会把设计模式通通讲一遍