设为首页 加入收藏

TOP

Python中命名空间与作用域使用总结(一)
2018-12-23 22:08:27 】 浏览:342
Tags:Python 命名 空间 作用 使用 总结

        命名空间,即Namespace,也成为名称空间或名字空间,指的是从名字到对象的一个映射关系,类似于字典中的键值对,实际上,Python中很多命名空间的实现用的就是字典。


  不同命名空间是相互独立的,没有任何关系的,所以同一个命名空间中不能有重名,但不同的命名空间是可以重名而没有任何影响。


        Python命名空间按照变量定义的位置,可以划分为以下3类:


  Built-in,内置命名空间,python自带的内建命名空间,任何模块均可以访问,存放着内置的函数和异常。


  Global,全局命名空间,每个模块加载执行时创建的,记录了模块中定义的变量,包括模块中定义的函数、类、其他导入的模块、模块级的变量与常量。


  Local,局部命名空间,每个函数、类所拥有的命名空间,记录了函数、类中定义的所有变量。


  一个对象的属性集合,也构成了一个命名空间。但通常使用objname.attrname的间接方式访问属性,而不是直接访问,故不将其列入命名空间讨论。(直接访问:直接使用名字访问的方式,如name,这种方式尝试在名字空间中搜索名字name。间接访问:使用形如objname.attrname的方式,即属性引用,这种方式不会在命名空间中搜索名字attrname,而是搜索名字objname,再访问其属性。)


  不同类型的命名空间有不同的生命周期:


  内置命名空间在Python解释器启动时创建,解释器退出时销毁;


  全局命名空间在模块被解释器读入时创建,解释器退出时销毁;


  局部命名空间,这里要区分函数以及类定义。函数的局部命名空间,在函数调用时创建,函数返回结果或抛出异常时被销毁(每一个递归函数都拥有自己的命名空间);类定义的命名空间,在解释器读到类定义(class关键字)时创建,类定义结束后销毁。(*


  作用域是针对命名空间而言,指命名空间在程序里的可应用范围,或者说是Python程序(文本)的某一段或某几段,在这些地方,某个命名空间中的名字可以被直接引用。这部分程序就是这个命名空间的作用域。只有函数、类、模块会产生新的作用域,代码块(例如iffor代码块)不会产生新的作用域。


  另外,python中变量的作用域是由它在源代码中的位置决定的(*)。由一个赋值语句引进的名字在这个赋值语句所在的作用域里是可见(起作用)的,而且在其内部嵌套的每个作用域内也可见,除非它被嵌套于内部的且引进同样名字的赋值语句所遮蔽。


  上述作用域的定义中表名了命名空间与作用于之间的关系:作用于是命名空间的可见范围。那么,在程序中访问某个名称时,是怎样一个搜索顺序呢?按照LEGB顺序搜索:


  Local首先搜索,包含局部名字的最内层(innermost)作用域,如函数/方法/类的内部局部作用域;


  Enclosing根据嵌套层次从内到外搜索,包含非局部(nonlocal)非全局(nonglobal)名字的任意封闭函数的作用域。如两个嵌套的函数,内层函数的作用域是局部作用域,外层函数作用域就是内层函数的 Enclosing作用域;


  Global倒数第二次被搜索,包含当前模块全局名字的作用域;


  Built-in最后被搜索,包含内建名字的最外层作用域。


  Python按照以上LEGB的顺序依次在四个作用域搜索名字,没有搜索到时,Python抛出NameError异常。所以:


  在局部作用域中,可以看到局部作用域、嵌套作用域、全局作用域、内建作用域中所有定义的变量。


  在全局作用域中,可以看到全局作用域、内建作用域中的所有定义的变量,无法看到局部作用域中的变量。


  在Python中,类定义所引入的作用域对于成员函数是不可见的,这与C++或者Java是很不同的,因此在Python中,成员函数想要引用类体定义的变量,必须通过self或者类名来引用它。(我的理解是Python类中所有变量有一个作用域,每个成员函数都有各自都作用域,这些作用域都是Local,且是平级的*)


  当在一个函数内部为一个变量赋值时,并不是按照上面所说LEGB规则来首先找到变量,之后为该变量赋值。在Python中,在函数中为一个变量赋值时,有下面这样一条规则:


“当在函数中给一个变量名赋值是(而不是在一个表达式中对其进行引用),Python总是创建或改变本地作用域的变量名,除非它已经在那个函数中被声明为全局变量. ”


那么,若想要在函数中修改全局变量,而不是在函数中新建一个变量,此时便要用到关键字global了。


  关键字nonlocal的作用与关键字global类似,使用nonlocal关键字可以在一个嵌套的函数中修改嵌套作用域中的变量,示例如下:


  第一,两者的功能不同。global关键字修饰变量后标识该变量是全局变量,对该变量进行修改就是修改全局变量,而nonlocal关键字修饰变量后标识该变量是上一级函数中的局部变量,如果上一级函数中不存在该局部变量,nonlocal位置会发生错误(最上层的函数使用nonlocal修饰变量必定会报错)。


  第二,两者使用的范围不同。global关键字可以用在任何地方,包括最上层函数中和嵌套函数中,即使之前未定义该变量,global修饰后也可以直接使用,而nonlocal关键字只能用于嵌套函数中,并且外层函数中定义了相应的局部变量,否则会发生错误。


  对上面代码略作修改:


  根据调用地方的不同,globals()和locals()函数可被用来返回全局和局部命名空间里的名字。


  如果在函数内部调用locals(),返回的是所有能在该函数里访问的命名。


  如果在函数内部调用globals(),返回的是所有在该函数里能访问的全局名字。


  两个函数的返回类型都是字典。所以名字们能用keys()函数摘取。


  上文介绍了变量名的搜索顺序是LEGB的,其中G、B两个作用域的引入在不能够通过代码操作的,能够通过语句引入的作用域只有E和L。Python中也只能函数???类的定义能引入新作用域。另外,在实际开发中,一定要主要函数定义引入local作用域或者Enclosing作用域中对应命名空间的声明周期。下面列举Python中的几例特殊情况。如果你觉得已经理解并掌握了上面命名空间与作用于的知识,请尝试解释下面的情况:


  推测出输出结果了吗?没错,会报错:NameError: name 'i' is not defined。切记:函数的命名空间在函数被调用时创建,函数执行完毕,命名就也被销毁。另外,LEGB搜索法则也不会让全局作用域去局部作用域寻找。


  if条件判断语句不会引入新的作用域,所以,语句“i=1”与“print(i)”属于同一作用域,既然同属于一个作用域,也不存在说if代码块运行完之后,作用域销毁,所以i一直存在,可以正常执行。


  for循环不会引入新的作用域,所以,循环结束后,继续执行print(i),可以正常输出i,原理上与情况3中的if相似。这一点Python就比较坑了,因此写代码时切忌for循环名字要与其他名字不重名才行。


  情况3中说到过,for循环不会引入新的朱用于,那么为什么输出报错呢?真相只有一个:列表生成式会引入新的作用域,for循环是在Local作用域里面的。事实上,lambda、生成器表达式、列表解析式也是函数,都会引入新作用域。


  在函数内部进行模块导入时,导入的模块只

首页 上一页 1 2 下一页 尾页 1/2/2
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇C#基础之异常处理及自定义异常 下一篇Python网络编程之socket模块基础..

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目