3.闭包
上面函数中,line()定义嵌套在另一个函数内部
如果函数的定义中引用了外部变量,会发生什么呢?
def line_conf():
b = 15
def line(x):
return 2*x + b
b = 5
return line #返回函数对象
if __name__ == '__main__':
my_line = line_conf()
print(my_line(5)) #打印15
可以看到,line()定义的隶属程序块中引用了高层级的变量b
b的定义并不在line()的内部,而是一个外部对象
我们称b为line()的环境变量,尽管b位于line()定义的外部
但当line被函数line_conf()返回时,还是会带有b的信息
一个函数的它的环境变量合在一起,就构成了一个闭包(Closure)
上面程序中,b分别在line()定义的前后有两次不同的赋值
上面的代码将打印15,也就是说,line()参照的是值为5的b值
因此,闭包中包含的是内部函数返回时的外部对象的值
在py中,所谓闭包是一个包含有环境变量取值的函数对象
环境变量取值被复制到函数对象的closure属性中
比如下面代码:
def line_conf():
b = 15
def line(x):
return 2*x + b
b = 5
return line #返回函数对象
if __name__ == '__main__':
my_line = line_conf()
print(my_line.__closure__)
print(my_line.__closure__[0].cell_contents)
(<cell at 0x1074379a8: int object at 0x10268f5d0>,)
5
可以看到,my_line()的closure属性中包含了一个元组
这个元组中的每个元素都是cell类型的对象
第一个cell包含的就是整数5,也就是我们返回闭包时的环境变量b的取值
闭包可以提高代码的可复用性,下面看四个函数:
def line1(x):
return x + 1
def line2(x):
return 4*x + 1
def line3(x):
return 5*x + 10
def line4(x):
return -2*x - 6
如果把上面的程序改为闭包,那么代码就会简单很多:
def line_conf(a,b):
def line(x):
return a*x + b
return line
line1 = line_conf(1,1)
line2 = line_conf(4,5)
line3 = line_conf(5,10)
line4 = line_conf(-2,-6)
这个例子中,函数line()与环境变量a,b构成闭包
在创建闭包的时候,我们通过line_conf()的参数说明直线的参量
这样,我们就能复用同一个闭包,通过代入不同的数据来获得不同的直线函数
如y=x+1和y=4x+5,闭包实际上创建了一群形式相似的函数
除了复用代码,闭包还能起到减少函数参数的作用:
def curve_closure(a,b,c):
def curve(x):
return a*x**2 + b*x + c
return curve
curve1 = curve_closure(1,2,1)
函数curve()是一个二次函数,它除了自变量x外
还有a,b,c三个参数,通过curve_closure()这个闭包
我们可以预设a,b,c三个参数,从而起到减参的效果
闭包的减参作用对于并行运算来说很有意义
在并行运算环境下,我们可以让每台电脑负责一个函数
把上一台电脑的输出和下一台电脑的输入串联起来
最终,我们像流水线一样工作,由于每台电脑只能接受一个输入
所以在串联之前,必须用闭包之类的方法把参数的个数降为1