Python中命名空间与作用域使用总结

命名空间与作用域是程序设计中的基础概念,深入理解有助于理解变量的生命周期,减少代码中的莫名其妙bug。Python的命名空间与作用域与Java、C++等语言有很大差异,若不注意,就可能出现莫名其妙的问题。

2 命名空间 2.1 什么是命名空间

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

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

2.2 命名空间的类型

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

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

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

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

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

2.3 命名空间的生命周期

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

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

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

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

3 作用域 3.1 什么是作用域

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

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

3.2 命名空间的查找顺序

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

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

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

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

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

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

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

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

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

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/5424570c827e43670b8e3a8ea68affcb.html