命名空间与作用域
# 名称空间
# 介绍
名称空间是用于存放变量名、函数名、类名等名称的地方。
名称空间在栈区中划分出来的三个空间,用来存放不同类别的名称。
三个空间分别是:内置名称空间、全局名称空间、局部名称空间。
把栈区中的名称分空间存放,就可以允许不同名称空间中有相同的名称。
# 内置名称空间
内置名称空间用于存放解释器内置的名字,如print、type、int等等名称。
存活周期:
Python解释器启动则产生。
Python解释器关闭则销毁。
# 全局名称空间
内置名称空间用于存放顶级定义的变量名、函数名、类名等名称。
不是类、函数体内定义的、又不是内置的,剩下的就是全局名称。
所以if、while、for中定义的名称也是全局名称。
存活周期:
Python文件执行到名称定义时则产生。
Python文件运行完毕后则销毁。
# 局部名称空间
存放函数内定义的变量名、函数名等的名称。
存活周期:
Python代码调用函数时则产生。
Python函数调用完毕后则销毁。
也就是调用一次就产生、销毁一次。
# 名称空间的加载顺序
内置名称空间 > 全局名称空间 > 局部名称空间
# 名称空间的销毁顺序
局部名称空间 > 全局名称空间 > 内置名称空间
# 名称空间的查找优先级
局部名称空间 > 全局名称空间 > 内置名称空间
在局部名称空间中,如果找得到则直接使用局部名称空间的名称。
如果局部名称空间没有,那就去全局名称空间找。
如果全局名称空间也没有,那就去内置名称空间找。
如果内置名称空间也没有,那就真的没有了。
对于嵌套函数的名称空间的关系,以函数定义阶段为准,与调用位置无关。
无论函数调用时是在嵌套的多少层,都是先从当前函数内开始,然后不断往上一层开始查找。
如果函数内,变量定义之前,就调用了变量,则不会查找上一层,而是报错。
b = 123
def a():
print(b)
b = 1
# 直接报错,即便全局空间有b变量。
print(a())
2
3
4
5
6
这是由于在定义阶段,python已经将函数内的名称,扫描并确定成了局部名称,所以局部空间内有这个名称,就不会再去上层找了。
# 作用域
# 全局作用域
能作用于全局,在全局都能有效。
内置名称空间、全局名称空间。
# 局部作用域
只能作用于局部,只在局部内有效。
局部名称空间。
# global关键字
声明要操作的是全局名称空间的名字。它可以在局部空间内修改全局名称所对应的值,或者在局部空间内定义新的全局名称与值。
一般用于不可变类型,对于可变类型一般没有必要使用global。
例如:
a = 10
def test():
global a
a = 20
test()
print(a)
>> 输出 20
2
3
4
5
6
7
# nonlocal关键字
声明要修改的是局部名称空间的名字。会从该层递归的往外层函数找对应的名称,最多会找到最外层的函数。
例如:
def test():
a = 10
def test2():
def test3():
nonlocal a
a = 20
test3()
test2()
print(a)
test()
>> 输出 20
2
3
4
5
6
7
8
9
10
11