在 Python 中,一切皆对象,命名空间实际是一个名称到对象的映射,另外,虽然 Python 是一种动态类型的语言,但是静态作用域语言,变量使用与作用域相关。
这里详细介绍命名空间和作用域的关系。
简介
在 Python 中,一切皆对象,而命名空间实际是一个名称到对象 (Objects) 的映射关系 (A namespace is a mapping from names to objects),多数的命名空间是用字典实现的,键就是变量名,值是那些变量的对象,也即变量的值。
另外,Python 是一种动态类型的语言,在编译阶段类型并没有与变量绑定,严格来说,变量名是与具体的变量绑定的。尽管是一种动态语言,但 Python 是静态作用域语言,也就是说,变量的作用域是由它在源代码中的位置决定的。
在使用一个变量之前不必先声明它,但是在真正使用它之前,必须已经绑定到某个对象;而名字绑定将在当前作用域中引入新的变量,同时屏蔽外层作用域中的同名变量,不论这个名字绑定发生在当前作用域中的哪个位置。
命名空间
命名空间包含了当前定义的符号名称及其所引用对象的集合,可以将命名空间看作字典,其中键是对象名称,而值就是对象本身。
在 Python 程序中的任何一个地方,都存在如下几个可用的命名空间:
- 局部命名空间,每个函数所有,包括了函数内定义的变量、参数;当函数被调用时创建一个局部命名空间,当函数返回结果或抛出异常时被删除;可以通过
locals()
函数访问。 - 全局命名空间,模块中定义,包括了模块内定义的函数、类、其它导入的模块、模块级的变量和常量,在模块导入时创建,会一直保存到解释器退出;可以通过
globals()
函数访问。 - 内置命名空间,在 Python 解析器启动时创建,会一直保留,任何模块均可访问,包含了一些常见的函数 (例如
sin()
、map()
、open()
等),异常 (例如BaseException
、IOError
等);在 Python3 中可以通过dir(builtins)
命令查看。
通过命名空间保存了变量名和值之间的映射关系,而变量的查找则与作用域相关。
作用域
变量的查询顺序为 LEGB ,也就是局部作用域 (Local),嵌套作用域 (Enclosing),全局作用域 (Global),内置作用域 (Build-in),在查询的过程中如果找到则停止搜索,否则抛出 NameError: name 'xxx' is not defined.
错误。
另外,在早些时候,Python 的是按照 LGB 查找的,后来由于闭包和嵌套函数的出现,于是又增加了嵌套作用域。
示例
内置作用域一般不建议修改 (实际可以通过 builtins
模块修改),可以通过如下的示例验证 LEG 的查找顺序。
float = "global scope" # <3>
def foobar():
float = "enclosing scope" # <2>
def bar():
float = "local scope" # <1>
print(float)
bar()
foobar()
直接运行会输出 local scope
,当注释掉 <1>
行后,会显示 enclosing scope
,再注释掉 <2>
之后,会显示 global scope
,如果再注释掉 <3>
行,那么就会输出内建的 float
对象,也就是 <class 'float'>
。