python闭包,自由变量
1.变量作用域
先看一个例子
>>> def func(a):
... print(a)
... print(b)
...
>>> func('hello')
hello
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in func
NameError: global name 'b' is not defined
这个错误我们都懂,我们没有定义变量b
在看下面的例子
>>> b = 'hello'
>>> def func(a):
... print(a)
... print(b)
... b = 'HELLO'
...
>>> func(1)
1
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in func
UnboundLocalError: local variable 'b' referenced before assignment
这里pirnt(a)可以执行,但是执行print(b)报错了。。 这是因为python在编译函数func的定义体时,发现有对变量b的定义,所以认为b是一个func函数内的局部变量,但是在执行到print(b)时,尝试获取局部变量b,但是b是在下面定义的 此时b还未绑定值所以获取失败报错。
如果此时在print(b)上面加个 global b python就会从全局变量中获取b的值‘hello’,并打印'hello'
2. 闭包与自由变量
闭包指延伸了作用域的函数,其中包含函数定义体中引用、但是不在定义体中定义的非全局变量。拿下图的装 饰器作为例子,内层函数averager的函数体中series就是一个列表的引用,但这个列表定义在了averager函数体外面,所以这就是个闭包,而变量series就是一个自由变量。自由变量指未在本地作用域中绑定的变量。
再看一下下面的例子
运行结果:
这个装饰器会将num的值加1,但是会报错,原因是num += 1相当于num= num + 1,这会隐式创建局部变量 num,但是内层函数里没有为num绑定值,所以报错。而上一个装饰器的例子不会报错,因为在averager函数内部只是调用了series.append()方法,并没有给series赋值。我们利用了列表是可变对象。所以, 我们在这个装饰器的内部函数只能做读取操作,不能更新。在python3里,为了解决这个问题引入了nonlocal声明,它是将变量标记为自由变量。如果为 nonlocal 声明的变量赋予新值,闭包中保存的绑定会更新。
这样就不会报错了
参考《流畅的python》