地位:
代码语言:javascript
复制闭包 和函数有关系
解释:
代码语言:javascript
复制python中一切皆对象:
函数可以赋值给变量,例如 a = def func(),
可以把函数当做参数,传入一个函数
可以把函数当做一个函数的返回结果
示例:
代码语言:javascript
复制Python中允许的正确的调用方法:
def curve_pre():
def curve():
print('This is a funcion')
return curve #函数作为返回值
func = curve_pre()
func()
#产生调用,输出 This is a funcion
将上述示例扩展为闭包:
代码语言:javascript
复制注意:
闭包内的变量与闭包外的变量没有关系
示例:
def curve_pre():
a = 25 #a在curve外部
def curve(x):
return a*x*x
return curve #函数curve作为返回值
func = curve_pre()
print(func(2)) #打印100
外部变量对一般函数的影响:
a = 10
def f(i):
return a*i
print(f(2)) #打印20
函数外面的a影响到了函数内a的值
外部变量对闭包的影响:
a = 10
print(func(2)) #打印100
调用外面的a没有影响到函数内a的值,def curve(x)内的a仍然是def curve_pre()内的a的值
上述就是闭包的现象
闭包定义:
代码语言:javascript
复制由函数以及函数定义时外部的变量构成的整体,叫闭包
闭包 = 函数 原函数所处环境的变量(原函数外部)
注意:
代码语言:javascript
复制上述函数所处环境的变量不能是全局变量,即:至少需要两个结构体嵌套
闭包内的环境变量:
保存在curve_pre().__closure__内
print(func.__closure__)
#输出:(<cell at 0x0000000001158CA8: int object at 0x00000000539604D0>,)
print(func.__closure__[0].cell_contents)
#输出:25
注意:
代码语言:javascript
复制单一函数 不同的外部变量 = 多种不同的闭包(类似设计模式的工厂模式)
闭包的调用方式:
代码语言:javascript
复制正常非闭包函数的调用:
代码:
def func1():
a = 10
def func2():
a = 20
print("func2's a = ",a) # 20 运行顺序:2
print("func1's a = ",a) # 10 运行顺序:1
func2()
print("func1's a = ",a) # 10 运行顺序:3
func1()
注意:
上述是一个函数的调用,不是一个闭包,可以使用__closure__来判断是否为闭包
测试是否是闭包:
def func1():
a = 10
def func2():
a = 20
return a
return func2
func2()
func1()
f = func1()
print(f.__closure__) #输出:None
原因:
func2中的a被当做了局部变量,此时func2函数内并没有产生对外部变量的引用!
所以,并没有构成一个闭包
修改为闭包的方式:
def func1():
a = 10
def func2():
#a = 20 将局部变量a注释
c = a * 20
return func2
func2()
func1()
f = func1()
print(f.__closure__)
#输出:(<cell at 0x0000000001108CA8: int object at 0x00000000539602F0>,)
成为闭包的原因:
将func2中的局部变量a去掉后,只要func2中产生对外部变量a的使用,就可以被作为闭包
闭包一定要引用外部环境的变量
闭包的应用:
代码语言:javascript
复制要求:
对于x,y 按顺序x=3,y=3;x=5,y=8;x=6,y=14
本质:
需要对中间变量进行保存
非闭包实现:(失败)
origin = 0
def walk(step):
new_pos = origin step #这一步origin是外面的全局变量
origin = new_pos
#此处的赋值会出错,因为如果函数内部有赋值操作,那么origin会变成局部变量,从而导致上一句中找不到origin的定义
return origin
print(walk(3))
print(walk(5))
print(walk(6))
上述代码修改为:(借助global,成功)
origin = 0
def walk(step):
global origin #显式的声明全局变量之后,就不会讲origin作为局部变量
new_pos = origin step
origin = new_pos
return origin
print(walk(3)) #3
print(walk(5)) #8
print(walk(6)) #14
闭包方式:(失败)
origin = 0
def func1(pos):
#pos成为了环境的变量
def walk(step):
new_pos = pos step
pos = new_pos
#此处报错UnboundLocalError: local variable 'pos' referenced before assignment
return new_pos
return walk
tour = func1(origin)
print(tour(3)) #3
print(tour(5)) #8
print(tour(6)) #14
修改为:(借助nonlocal,成功)
origin = 0
def func1(pos):
#pos成为了环境的变量
def walk(step):
nonlocal pos #显式声明pos是一个本地变量
new_pos = pos step
pos = new_pos
#此处报错UnboundLocalError: local variable 'pos' referenced before assignment
return new_pos
return walk
tour = func1(origin)
print(tour(3)) #3
print(tour.__closure__[0].cell_contents) #3
print(tour(5)) #8
print(tour.__closure__[0].cell_contents) #8
print(tour(6)) #14
print(tour.__closure__[0].cell_contents) #14
使用闭包的优点:(函数式编程)
没有使用全局变量origin,所有的变量操作均在闭包内部
闭包 nonlocal关键字可以完成中间变量的记录,打印__closure__[0].cell_contents也会发现,闭包确实记录了中间变量
闭包的扩展:
代码语言:javascript
复制可以实现设计模式中的;工厂模式
闭包内的变量会常驻内存,使用时要注意
闭包不是函数式编程的全部,只是一种体现