1. 命名空间 (Namespace)
Python 中的 命名空间 (Namespace) 和作用域是密切相关的概念。Python 命名空间 (Namespace) 可以视为一个字典,其中键是变量名,值是与之关联的对象。各个命名空间是独立的,同一个命名空间中不能有重名(重名的以后一个为准),不同的命名空间是可以重名。
命名空间类似于电脑的文件系统,一个文件夹(目录)中可以包含多个文件夹,每个文件夹中不能有相同的文件名,但不同文件夹中的文件可以重名。
一般有三种命名空间:
内置名称 (built-in names): Python 语言内置的名称,比如函数名 abs、char 和异常名称 BaseException、Exception 等;
全局名称 (global names): 模块中定义的名称,记录了模块的变量,包括函数、类、其它导入的模块、模块级的变量和常量;
局部名称 (local names): 函数中定义的名称,记录了函数的变量,包括函数的参数和局部定义的变量。
命名空间查找顺序: 局部的命名空间 -> 全局命名空间 -> 内置命名空间。
命名空间的生命周期:命名空间的生命周期取决于对象的作用域,对象执行完成,则该命名空间的生命周期就结束。
2. 作用域
作用域就是 Python 程序可以直接访问命名空间的范围,或者说变量的有效范围。Python 的作用域有 4 种:
L(Local):最内层,包含局部变量,比如一个函数/方法内部。
E(Enclosing):包含了非局部 (non-local) 也非全局 (non-global) 的变量。比如两个嵌套函数,一个函数(或类) A 里面又包含了一个函数 B ,那么对于 B 中的名称来说 A 中的作用域就为 nonlocal。
G(Global):当前脚本的最外层,比如当前模块的全局变量。
B(Built-in): 包含了内置的变量/关键字等。
作用域查找遵循 LEGB 规则:
Local -> Enclosing -> Global -> Built-in
内置作用域是通过一个名为 builtins 的标准模块来实现的,查看内置作用域里的变量和关键字,可以运行如下代码:
import builtins
print(dir(builtins))
注:可是使用 dir() 函数查看模块、类、对象等结构内部所包含的属性列表和方法列表。
Python 中只有模块(module)、类(class)和函数(def、lambda)才会引入新的作用域,其它的代码块(比如 if/elif/else/、try/except、for/while 等)不会引入新的作用域。
示例:
#!/usr/bin/python3 # -*- coding: UTF-8 -*- # 全局变量 s = 'Global' def func(): # nonlocal 变量 s = 'Enclosing' def inner_func(): # 局部变量 s = 'Local' print(s) # 输出局部变量 s inner_func() print(s) # 输出 nonlocal 变量 s if __name__ == "__main__": func() print(s) # 输出全局变量 s print(x) # 输出未定义变量 x
输出结果如下:
Local Enclosing Global Traceback (most recent call last): File "d:/pythonDemo/func3.py", line 22, in <module> print(x) # 输出未定义变量 x NameError: name 'x' is not defined
注:在各命名空间都找不到变量 x, 抛出了一个 NameError 异常。
在 fun() 作用域内,给 s 赋值 'Enclosing',没有改变全局变量 s 的值。在 inner_func() 作用域内,给 s 赋值 'Local',没有改变nonlocal 变量 s 的值。在作用域内,想要修改作用域外的变量,需要用到 global 或 nonlocal 关键字。
3. global 和 nonlocal 关键字
在 Python 中,global 关键字用于标识变量是全局的。如果在函数内部要修改全局变量,需要使用 global 关键字。
nonlocal 关键字是用于标识变量是外部的(非全局)。如果在函数内部要修改外部函数的变量,需要使用 nonlocal 关键字。
示例1:
#!/usr/bin/python3 # -*- coding: UTF-8 -*- # 全局变量 s = 'Global' def func(): # nonlocal 变量 s = 'Enclosing' def inner_func(): # 标识 s 是全局变量 global s s = 'Local' print(s) # 输出全局变量 s inner_func() print(s) # 输出 nonlocal 变量 s if __name__ == "__main__": func() print(s) # 输出全局变量 s
输出结果如下:
Local
Enclosing
Local
示例2:
#!/usr/bin/python3 # -*- coding: UTF-8 -*- # 全局变量 s = 'Global' def func(): # nonlocal 变量 s = 'Enclosing' def inner_func(): # 标识 s 是 nonlocal 变量 nonlocal s s = 'Local' print(s) # 输出 nonlocal 变量 s inner_func() print(s) # 输出 nonlocal 变量 s if __name__ == "__main__": func() print(s) # 输出全局变量 s
输出结果如下:
Local
Local
Global
4. 获取作用域范围中的变量
1) globals() 函数
globals() 函数是 Python 的内置函数,在函数内外调用(包括全局作用域),都返回一个全局变量字典。
示例:
#!/usr/bin/python3 # -*- coding: UTF-8 -*- # 全局变量 a = 1 b = 2 c = 3 def func(): # nonlocal 变量 i = 11 j = 12 def inner_func(): # 局部变量 x = 101 y = 102 inner_func() if __name__ == "__main__": print(globals())
输出结果如下:
{... , 'a': 1, 'b': 2, 'c': 3, ...}
注: globals() 函数返回的字典中,会默认包含有很多下划线变量,它们是 Python 主程序内置的属性和方法,不用管它们。
2) locals() 函数
locals() 函数也是 Python 的内置函数,在函数内外调用,返回一个当前作用域内所有变量的字典。
示例:
#!/usr/bin/python3 # -*- coding: UTF-8 -*- # 全局变量 a = 1 b = 2 c = 3 def func(): # nonlocal 变量 i = 11 j = 12 print(locals()) def inner_func(): # 局部变量 x = 101 y = 102 print(locals()) inner_func() if __name__ == "__main__": func() print(locals())
输出结果如下:
{'i': 11, 'j': 12}
{'x': 101, 'y': 102}
{... , 'a': 1, 'b': 2, 'c': 3, ...}
注: 在函数内部调用 locals() 函数,会获得包含所有局部变量的字典;在全局作用域调用,locals() 的功能和 globals() 函数相同。
3) vars(object) 函数
vars(object) 函数也是 Python 内置函数,其功能是返回一个指定 object 对象范围内所有变量组成的字典。
示例:
#!/usr/bin/python3 # -*- coding: UTF-8 -*- # 全局变量 a = 1 b = 2 c = 3 class Person: name = 'Python' age = 20 if __name__ == "__main__": print(vars(Person)) print(vars())
输出结果如下:
{..., 'name': 'Python', 'age': 20, ...}
{ ... 'a': 1, 'b': 2, 'c': 3, ...}
注:如果不传入 object 参数,vars() 和 locals() 的作用完全相同。