参考博客:https://www.cnblogs.com/JohnABC/p/4076855.html
一、概述
概念:如果一个内部函数对外部作用域的自由变量进行了引用或使用,那么这个内部函数就称为闭包(closure)。
我个人认为,闭包存在的意义就是它夹带了外部变量(私货),如果它不夹带私货,它和普通的函数就没有任何区别。同一个的函数夹带了不同的私货,就实现了不同的功能。其实你也可以这么理解,闭包和面向接口编程的概念很像,可以把闭包理解成轻量级的接口封装。【引自:https://www.cnblogs.com/JohnABC/p/4076855.html】
支持将函数当成对象使用的编程语言,一般都支持闭包。比如Python, JavaScript。
下面利用闭包表示一元二次曲线。
#-*-coding:utf8-*-
def curve(a,b,c):
def cur(x):
return a*x*x+b*x+c
return cur
#求具体a,b,c所对应的曲线
curve1=curve(2,1,1)
#利用列表解析求曲线的多个坐标值
print [(x,curve1(x)) for x in range(5)]
#根据输入x的值求对应的坐标
x=input('please enter x:')
print (x,curve1(x))
#闭包函数相对与普通函数会多出一个__closure__的属性,里面定义了一个元组用于存放所有的外部变量
curve1.__name__ #'cur'
curve1.__closure__
'''结果为
(<cell at 0x0638C330: int object at 0x02AC7A5C>,
<cell at 0x0638C7D0: int object at 0x02AC7A68>,
<cell at 0x0638C7B0: int object at 0x02AC7A68>)
'''
curve1.__closure__[0].cell_contents #2
curve1.__closure__[1].cell_contents #1
curve1.__closure__[2].cell_contents #1
上面代码中函数cur与外部变量a,b,c形成了闭包。利用闭包可以轻松地表达任意系数的一元二次曲线,进而求出曲线上的任意坐标点。如果不利用闭包,每次创建一元二次曲线函数时都需要同时说明a,b,c,x,也就需要更多的参数传递。降低了代码的可移植性。
二、注意的事项
1、闭包中是不能修改外部作用域的局部变量的
def foo():
m=0
def foo1():
m=2
print m
print m #0
foo1() #2
print m #0
2、Python3后使用nonlocal
关键字可以修改外层函数中变量的值,如使用语句nonlocal a就可以显式的指定a不是闭包的局部变量。
def foo():
m=0
def foo1():
#python3中使用nonlocal
nonlocal m
m = 2
print(m)
print(m) #0
foo1() #2
print(m) #2
3、还有一个容易产生错误的事例
flist = []
for i in range(3):
def foo(x): print x + i
flist.append(foo)
for f in flist:
f(2)
上面代码期望的结果应该是2,3,4,但是结果为:4,4,4。原因在于函数只有在执行时,才会寻找函数里面变量的值。当把函数追加到flist列表时,函数还没有给i赋值,只有当函数执行时,才会去找i的值是多少。但这时在for循环后,i的值已经变为2了,以上。解决方法如下:
flist = []
for i in range(3):
def foo(x,y=i): print x + y
flist.append(foo)
for f in flist:
f(2)
三、应用(抄的)
1、当闭包执行完后,仍然能够保持住当前的运行环境。----现在还不是很明白,再看。
比如说,如果你希望函数的每次执行结果,都是基于这个函数上次的运行结果。我以一个类似棋盘游戏的例子来说明。假设棋盘大小为50*50,左上角为坐标系原点(0,0),我需要一个函数,接收2个参数,分别为方向(direction),步长(step),该函数控制棋子的运动。棋子运动的新的坐标除了依赖于方向和步长以外,当然还要根据原来所处的坐标点,用闭包就可以保持住这个棋子原来所处的坐标。
origin=[0,0]
legal_x=[0,50]
legal_y=[0,50]
def position(pso=origin):
def player(direction,setp):
legal_x=direction[0]*setp+pos[0]
legal_y=direction[1]*setp+pos[1]
#注意!此处不能写成 pos = [new_x, new_y]
pos[0]=legal_x
pos[1]=legal_y
return pos
return player
player = create() # 创建棋子player,起点为原点
print player([1,0],10)
print player([0,1],20)
print player([-1,0],10)
2、闭包可以根据外部作用域的局部变量来得到不同的结果,这有点像一种类似配置功能的作用,我们可以修改外部的变量,闭包根据这个变量展现出不同的功能。比如有时我们需要对某些文件的特殊行进行分析,先要提取出这些特殊行。
def make_filter(keep):
def the_filter(file_name):
file = open(file_name)
lines = file.readlines()
file.close()
filter_doc = [i for i in lines if keep in i]
return filter_doc
return the_filter
如果我们需要取得文件"result.txt"中含有"pass"关键字的行,则可以这样使用例子程序
filter = make_filter("pass")
filter_result = filter("result.txt")