用一个类比来理解命名空间与作用域:
四种作用域相当于我们生活中的国家(Built-in)、省(Global)、市(Enclosing)、县(Local),命名空间相当于公务员花名册,记录着哪个职位是哪个人。国家级公务员服务于全国
民众(全国老百姓都可以喊他办事),省级公务员只服务于本身民众(国家层面的人或者其他省的人我不管),市(Enclosing)、县(Local)也是一个道理。当我们要找某一类领导(例如想找
个警察帮我打架)时(要访问某个名称),如果我是在县(Local)里头,优先在县里的领导花名册中找(优先在自己作用域的命名空间中找),县里花名册中没警察没有就去市里的花名册找(往
上一层作用域命名空间找),知道找到国家级都还没找到,那就会报错。如果省级民众想找个警察帮忙大家,不会找市里或者县里的,只会找自己省里的(其它省都不行),或者找国家级的。国家、
省、市、县肯定一直都在那里,可不会移动(作用域是静态的);领导可以换届,任期移到就换人(命名空间是动态的,每次调用函数都会新的命名空间,函数执行结束,命名空间销毁)。
当在一个函数内部为一个变量赋值时,并不是按照上面所说LEGB规则来首先找到变量,之后为该变量赋值。在Python中,在函数中为一个变量赋值时,有下面这样一条规则:
“当在函数中给一个变量名赋值是(而不是在一个表达式中对其进行引用),Python总是创建或改变本地作用域的变量名,除非它已经在那个函数中被声明为全局变量. ”
那么,若想要在函数中修改全局变量,而不是在函数中新建一个变量,此时便要用到关键字global了。
i = 1 def func(): global i print(i) #输出1 i = 2 func() print(i) #输出2
关键字nonlocal的作用与关键字global类似,使用nonlocal关键字可以在一个嵌套的函数中修改嵌套作用域中的变量,示例如下:
def f1(): i = 1 def f2(): nonlocal i print(i) #输出1 i = 2 f2() print(i) f1() #输出2
第一,两者的功能不同。global关键字修饰变量后标识该变量是全局变量,对该变量进行修改就是修改全局变量,而nonlocal关键字修饰变量后标识该变量是上一级函数中的局部变量,如果上一级函数中不存在该局部变量,nonlocal位置会发生错误(最上层的函数使用nonlocal修饰变量必定会报错)。
第二,两者使用的范围不同。global关键字可以用在任何地方,包括最上层函数中和嵌套函数中,即使之前未定义该变量,global修饰后也可以直接使用,而nonlocal关键字只能用于嵌套函数中,并且外层函数中定义了相应的局部变量,否则会发生错误。
对上面代码略作修改:
i = 0 def f1(): i = 1 def f2(): global i #此处改为glocal print(i) #输出0 i = 2 f2() print(i) f1() #输出2
3.4 globals()和locals()函数根据调用地方的不同,globals()和locals()函数可被用来返回全局和局部命名空间里的名字。
如果在函数内部调用locals(),返回的是所有能在该函数里访问的命名。
如果在函数内部调用globals(),返回的是所有在该函数里能访问的全局名字。
两个函数的返回类型都是字典。所以名字们能用keys()函数摘取。
4 易错情况上文介绍了变量名的搜索顺序是LEGB的,其中G、B两个作用域的引入在不能够通过代码操作的,能够通过语句引入的作用域只有E和L。Python中也只能函数���类的定义能引入新作用域。另外,在实际开发中,一定要主要函数定义引入local作用域或者Enclosing作用域中对应命名空间的声明周期。下面列举Python中的几例特殊情况。如果你觉得已经理解并掌握了上面命名空间与作用于的知识,请尝试解释下面的情况:
(1)情况1:def test(): i = 0 test() print(i)