命名空间(namespace)是从命名到对象的映射。Python当前命名空间主要是通过字典实现的,以后有可能会改变其实现方式。某种意义上讲对象的属性集也是一个命名空间。不同命名空间中的命名没有任何联系。

不同的命名空间在不同的时刻创建,有不同的生存期。包含内置命名的命名空间在Python解释器启动时创建,会一直保留,不被删除。模块的全局命名空间在模块定义被读入时创建,通常,模块命名空间也会一直保存到解释器退出。由解释器在最高层调用执行的语句,不管它是从脚本文件中读入还是来自交互式输入,都是 __main__ 模块的一部分,所以它们也拥有自己的命名空间(内置命名也同样被包含在一个模块中,它被称作 builtins )。

当调用函数时,就会为它创建一个局部命名空间,并且在函数返回或抛出一个并没有在函数内部处理的异常时被删除。(实际上,用遗忘来形容到底发生了什么更为贴切。)当然,每个递归调用都有自己的局部命名空间。

作用域(scope)就是一个 Python 程序可以直接访问命名空间的正文区域。这里的直接访问意思是一个对名称的错误引用会尝试在命名空间内查找。尽管作用域是静态定义,在使用时他们都是动态的。每次执行时,至少有三个命名空间可以直接访问的作用域嵌套在一起:

作用域决定于源程序:一个定义于某模块中的函数的全局作用域是该模块的命名空间,而不是该函数的别名被定义或调用的位置,了解这一点非常重要。另一方面,命名的实际搜索过程是动态的,在运行时确定的——然而,Python 语言也在不断发展,以后有可能会成为静态的“编译”时确定,所以不要依赖动态解析!(事实上,局部变量已经是静态确定了。)

如果没有使用global语法,其赋值操作总是在最内层(innermost)的作用域。赋值不会复制数据,只是将命名绑定到对象。删除也只是从局部作用域的命名空间中删除命名x。事实上,所有引入新命名的操作都作用于局部作用域。特别是import语句和函数定义将模块名或函数绑定于局部作用域(可以使用global语句将变量引入到全局作用域)。

如果一个命名声明为全局的,那么对它的所有引用和赋值会直接搜索包含这个模块全局命名的作用域。如果要重新绑定最里层作用域之外的变量,可以使用 nonlocal 语句;如果不声明为 nonlocal,这些变量将是只读的(对这样的变量赋值会在最里面的作用域创建一个新的局部变量,外部具有相同命名的那个变量不会改变)。

global 语句用以指明某个特定的变量为全局作用域,并重新绑定它。nonlocal 语句用以指明某个特定的变量为封闭作用域,并重新绑定它。

查看所有全局变量、局部变量的方法:

globals()以字典类型返回当前位置的全部全局变量

locals()以字典类型返回当前位置的全部局部变量

常用的内置全局变量:

__doc__ :获取文件的注释

__dict__:包含了对象里可用的属性-属性值的字典

__file__ :获取当前位置文件的路径

__cached__ :获取当前位置文件的缓存文件路径

__name__ :获取当前位置的模块名称,如果当前是主模块则返回'__main__'

LEGB-rule

在一个Python程序运行中,至少有4个scopes是存在的。直接访问一个变量可能在这四个namespace中逐一搜索。

Local(innermost):包含局部变量。比如一个函数/方法内部。

Enclosing:包含了非局部(non-local)也非全局(non-global)的变量。如同级函数

Global(next-to-last):当前脚本的最外层。

Built-in(outtermost):Python的内建模块builtins模块(python2.X中内建模块名称为builtin),内建模块有一个引用(别名)为__builtins__,内建模块在Python启动后且没有执行程序员所写的任何代码前会自动加载到内存,即python中我们称之为内置变量、内置方法和内置类的都在该模块中,可以直接使用,不用在其前添加内建模块前缀。

”LEGB-rule”,即scope的搜索顺序:Local -> Enclosing -> Global -> Built-in

当有一个变量在 local 域中找不到时,Python会找上一层的作用域,即 enclosing 域(该域不一定存在)。enclosing 域还找不到的时候,再往上一层,搜索模块内的 global 域。最后,会在 built-in 域中搜索。对于最终没有搜索到时,Python会抛出一个 NameError 异常。

作用域可以嵌套。比如模块导入时。

 作者:qingheluo