学习过程中,我们会遇到两个有趣的关键字,一个是global,一个是nonlocal。
如果直接对英文进行翻译,一个会被翻译成“全局”,一个被翻译成“非局部”。
无论怎么想,都感觉两个意思差不多。这就很有趣了,就像是在讨论“美”和“不丑”的区别。
其实两个关键字功能都是在划定变量的定义域,需要视场景需要而定。
先试看一个例子:
>>> b = 1
>>> def f():
print(b)
>>> f()
特别简单,一下就得出了结果。f函数输出了全局变量b的值1。
>>> b = 1
>>> def f():
print(b)
b = 2
>>> f()
与上一个例子相比,f函数中,多了一步对变量b的赋值。那它的结果是什么呢?
首先输出全局变量b的值1,然后对b重新赋值2?
嘿,这就奇怪了。第一个例子里面我也没有定义局部变量b,怎么它就能去找全局变量b呢?
这就要说到Python的变量作用域规则:假定函数定义体中赋值的变量是局部变量。
换而言之啊,因为“b=2”语句的存在,让Python认定f中的b属于局部变量。
那如何在函数中使用全局变量呢?用global标记一下:
告诉函数,这个变量是个全局变量,接下来的操作都是对全局变量b而言的。
全局的问题解决了,再来看另外一个例子:
>>> def f():
count = 0
def a():
count += 1
print(count)
return a()
>>> f()
猜猜看结果是什么?
有了前面的分析,这个例子就很好得出结果了。依旧是报错嘛。
因为函数a中,对count变量进行了赋值,所以Python认定count是局部变量,但在执行“count + 1”操作时,却没有找到count,所以报错了。
那怎么处理呢?
我猜你会用global标记一下,但是注意啊。global标记的是全局变量,而这个例子中的count是a函数上一级f函数的变量,并不是全局变量。
对比global,这里要用 nonlocal标记,告诉a函数,count不在本地定义,但也不在全局变量里。
这个词想必在学习过程中一直听到,它是什么意思呢?简单来说啊,闭包是指延伸了作用域的函数。
我们回头看最后一个例子:
>>> def f():
count = 0
def a():
nonlocal count
count += 1
print(count)
return a()
>>> f()
调用函数f时,f会返回a(),当执行a()的时候,f函数的任务已经完成了,它占用的资源就会释放掉,这其中包括了函数f环境中定义的count变量。
也就是说,在执行a的时候,函数f已经“撒手人寰”了,没有办法再去找到其中的数据。
像函数a中count这一类,不在本地定义域中绑定的变量,我们叫做自由变量。(和线性代数里的自由变量没关系哈。)
用nonlocal标记后呢,Python就会保留自由变量的绑定(或者说定义环境)。
因此最后例子中,整个函数a的作用域是“count = 0”,加上a本身。
因为a延伸了自己的作用域,所以这里就可以被称作“闭包”。
global和nonlocal的功能都是改变变量作用域。
global标记全局变量,nonlocal标记非全局、非本地的自由变量。
延伸了作用域的函数,被称作“闭包”