python的闭包
*从作用域的概念开始:
全局作用域:整个程序运行环境中都可见。
局部作用:函数、类内部可见。
两者特点:
1.局部变量使用范围不能超过其所在的局部作用域。
2.外层变量作用域在内层作用域可见。在内层作用域定义一个同名的局部变量时,相当于在当前作用域重新定义一个新的变量,但不会覆盖外层作用域的同名变量。举例如下:
x = 5 def foo(): y = x + 1 print(y) foo()
输出的结果是6;因为在foo()函数内引用到外部作用域的x变量。
x = 5 def foo(): x += 1 print(x) foo()
这时候会报错:local variable 'x' referenced before assignment。为什么呢?因为x += 1等价于 x = x + 1,相当于在foo()函数内部重新定义一个局部变量x,那么foo()函数内部所有x都是这个局部变量x,但这个x还没有完成赋值,就被进行加1操作。
(python动态语言中赋值才算定义,才能被引用。这里x没有赋值就无法被引用)
解决办法:
x = 5 def foo(): global x x += 1 print(x) foo()
global将foo内的x声明为使用外部的全局作用域中定义的x。此时就会成功输出6。那么如果foo外部没有x变量的声明,是否还会报错呢?
def foo(): global x x = 10 x += 1 print(x) foo()
此时会成功输出11。说明:一旦作用域中使用global关键字声明为全局的,那么x=10相当于在为全局作用域的变量x赋值。
*关键字global
1.使用global关键字的变量,将会被声明为外部的全局作用域中定义的变量。
2.外部作用域的变量会在内部作用域中可见,但是也不要在内部的局部作用域中直接使用,因为函数的目的是为了封装,尽量与外界隔离。
3.如果函数需要使用外部全局变量,使用函数的形参传参解决。
*闭包概念
自由变量:未在本地作用域(非全局作用域!)中定义的变量。例如:定义在内层函数外的外层函数的作用域中的变量。
闭包:内层函数引用到外层函数的自由变量。(最常见的就是JavaScript)
python2中实现闭包的举例如下:
def counter(): c = [0] def inc(): c[0] += 1 return c[0] return inc
foo = counter()
print(foo(), foo()) # 打印1 2
c = 100
print(foo()) # 打印3
此时c[0] += 1并不会像上面的x += 1情况报错。为什么呢?因为c已经在外层函数counter()里定义过,内层函数inc()里并不是对c的重新定义,而是对列表c内的元素进行修改。
c=100中的c和counter()中的c不一样,inc()引用的是自由变量,正是counter()中的变量c。
上面的例子是针对dict,list这些可变对象的闭包,但是对于int,string,float,tuple这些python中不可变对象变量的闭包,python3里可以使用nonlocal实现。
nonlocal:使用nonlocal关键字,将变量标记为上级的局部作用域中定义,但不能是全局作用域中定义的变量。
def counter(): count = 0 def inc(): nonlocal count count += 1 return count return inc foo = counter() print(foo()) # 1
print(foo()) # 2
该例子就是一个正常使用的闭包。
a = 10 def counter(): nonlocal a a += 1 print(a) count = 0 def inc(): nonlocal count count += 1 return count return inc foo = counter() foo() foo()
nonlocal a中a的上一级作用域是全局作用域,所以代码不能正常运行。