引入
在python解释器中运行一行代码import this
就可以看到“传说”中的python之禅,它体现了使用python进行开发的规范,而最后一句 - Namespaces are one honking great idea -- let's do more of those!就是本文的主角。
名称空间(Namespaces)
名称空间就是存放名字与对象绑定关系的地方,是对栈区的划分。定义一个变量。name = 'python',解释器会申请内存空间存放值'python',将名字name与'python'的内存地址的绑定关系存在栈区中。
有了名称空间之后就可以在栈区中存放相同的名字,在程序执行期间最多会存在三种名称空间,每种名称空间相互独立。
内置名称空间(built-in)
存放的是python解释器内置的名字,比如python中的保留字(关键字)。python解释器启动时内置名称空间就会产生,python解释器关闭,则销毁。
# 在交互式的解释器环境中输入print然后回车
>>> print
<built-in function print>
全局名称空间
只要不是函数内定义也不是内置的名字,剩下的全部都是全局名称。全局名称空间在运行python文件时产生,python文件执行完成之后销毁。
x = 1 # x是全局名称
def func(): # func是全局名称
print(x) # print是内置名称
局部名称空间
函数内定义的名字都会被存放在局部名称空间。局部名称空间在调用函数时产生,函数运行完毕后销毁。
def func(): # func全局名称
x = 100 # x局部名称
print(x) # print内置名称
名称空间与作用域
名称空间的加载顺序是:内置名称空间->全局名称空间->局部名称空间。
名称空间销毁顺序是:局部名称空间->全局名称空间->内置名称空间。
按照名称空间中名字的作用范围可以将三种名称空间划分为两个区域。
全局作用域---全局名称空间、内置名称空间中的名字属于全局作用域,全局作用域中的名字在整个文件执行过程中都不会被销毁,在任意位置都可以使用。
局部作用域---位于局部名称空间中的名字属于局部作用域,局部作用域中的名字在函数调用时产生,调用结束时销毁,只能在函数内使用。
作用域与名字查找优先级
名字顺序的查找需要记住一条准则:名字的查找顺序以函数阶段为准,与调用阶段没有关系。
- 从局部作用域开始
在局部作用域查找名字,起始位置是局部作用域,现在局部名称空间中查找,找不到再去全局作用域查找,先查找全局名称空间,找不到再去内置名称空间,最后都找不到会抛出异常。
- 从全局作用域开始
在全局作用域查找名字时,起始位置就是全局作用域,先查找全局名称空间,找不到再去内置名称空间,最后都找不到会抛出异常。
- 名称空间的“嵌套关系”
这里的嵌套可以理解为不同的名称空间都存在同样的变量名,注意:一定要以函数定义阶段为准,并且牢记名称空间的查找顺序。
global & nonlocal
如果想要在函数内修改全局名称空间中名字的值并且该值是不可变数据类型,需要用到global关键字,如果该值是可变数据类型,则无需使用global,可以直接对原值进行修改。
x = 100
l = [1, 2]
def func():
global x # 声明x是全局名称空间的名字
x = 101
l.append(3)
func()
print(x) # 101
print(l) # [1, 2, 3]
对于嵌套多层的函数,可以使用nonlocal关键字对外层嵌套函数定义的名字进行声明并修改其值。nonlocal会从当前函数的外层函数一层一层去查找声明的名字,如果在最外层函数的名称空间中找不到该名字,会抛出异常。
def func():
x = 10
def func1():
nonlocal x
x = 11
func1() # 修改func作用域中x的值
print(x) # 在func作用域中查看x的值
func() # 11