闭包
如果在一个内部函数里,对在外部作用域(但不是在全局作用域)的变量进行引用,那么内部函数就被认为是闭包(closure)
引用>这里是一个闭包的例子:
<代码类="语言python "> def addx (x): def加法器(y): 返回x + y 返回加法器 if __name__==癬_main__”: func=addx (10) 打印(func (1)) 打印(func (2)) 打印(func(3) 代码>执行结果:
<代码> 11 12 13 代码>这里例子里,加法器(y)就是一个内部函数。它里面引用了外部的变量x, x是外部作用域addx (x)里的变量,但是不是全局变量,所以内部的加法器(y)是一个闭包。
精炼一些:闭包=函数块+定义函数时的环境.adder就是函数块,x就是环境。闭包的注意事项
作用域的问题
这个例子里应该是一个函数的作用域的问题,和闭包没太大关系:
<代码类="语言python "> def foo (): x=0 def f (): x=1 返回x 打印(x) # 0 打印(f ()) # 1 打印(x) # 0 if __name__==癬_main__”: foo() 代码>内部函数和外部函数都定义了变量x。这里内部没有引用外部的变量x,还是生成了一个自己的局部变量,也叫x,这个变量还外部的函数的x变量是没有关系的,所以这里的问题只是一个作用域的问题。
操作外部变量
下面的这个函数是有问题的,语法有错误:
<代码类="语言python "> def广场(): x=0 def f (): x=x + 1 返回x * 返回f 代码>看似符合闭包的要求,但是标量x出现在了赋值符号‘=淖蟊?python规则指定所有在赋值语句左面的变量都是局部变量。这里因为x被认为是局部变量,然后再执行x + 1的时候就只会在局部里找这个x的值,但是找不到,所以就报错了,错误信息如下:
<代码> UnboundLocalError:局部变量“x”引用之前赋值代码><>强解决方案1 强>
避免直接引用外部,如果引用的是外部的列表,字典等。那么变量名就不会直接出现在赋值符号左边了:<代码类="语言python "> def广场(): x=[0] def f (): x [0]=x [0] + 1 返回x [0] * [0] 返回f 代码>如果直接要引用的是外部的列表,字典这类变量,用就不会遇到这类问题。但是这里例子里,这么做感觉也不好
<>强解决方案2 强>
如果要引用的外部变量就是一个简单的数值或者字符串,虽然上面的方法可行,但是还有更好的做法。
使用外地声明,把内层的局部变量设置成外层局部可用,但是还不是全局的。类似声明全局变量的全球的用法。这里主要是因为python里不需要像其他语言里,有类型的var之类的关键字来声明变量。变量直接赋值就完成了声明,平时用起来很方便,但是在这里,因为在操作的时候变量直接出现在赋值符号左边了,就会被认为新定义了一个局部变量了。
完整的示例:<代码类="语言python "> def广场(): x=0 def f (): 外地x x=x + 1 返回x * 返回f if __name__==癬_main__”: func=广场() print (func ()) print (func ()) print (func()) 代码>闭包的作用
闭包主要是在函数式开发过程中使用。下面介绍的两种使用场景,用面向对象也是可以很简单的实现的。但是在用Python进行函数式编程时,闭包对数据的持久化以及按配置产生不同的功能,是很有帮助的。
让函数还可以拥有状态
当闭包执行完后,仍然能够保持住当前的运行环境。上面也提过了:闭包=函数块+定义函数时的环境。这个环境可以在闭包里改变,并且保持下去。
下面是一个类似移动棋子的例子。先在坐标0,0创建棋子。然后可以用内部函数移动棋子。移动后,棋子的状态也更新了,下次再移动棋子,就是在之前的位置的基础上再进行的移动:<代码类="语言python "> def create_piece (): x=0 y=0 def移动(offset_x=0, offset_y=0): 外地x, y x +=offset_x y +=offset_y 返回x, y 返回移动 if __name__==癬_main__”: 球员=create_piece() #这里是不是和面向对象里的使用前,生成对象的实例很像吗? 打印(球员())#打印当前坐标 球员(1,1)#移动棋子 打印(球员())#打印当前坐标 打印(球员(1、3)#再移动棋子并打印坐标Python闭包