一、名称空间
名称空间(namespacs) :存放名字的地方,是对栈区的划分。
有了名称空间之后,就可以在栈区中存放相同的名字,详细的名称空间分为三种:内置名称空间,全局名称空间,局部名称空间
L —— Local(function);函数内的名字空间
E —— Enclosing function locals;外部嵌套函数的名字空间(例如closure)
G —— Global(module);函数定义所在模块(文件)的名字空间
B —— Builtin(Python);Python内置模块的名字空间
#builtin B —— Builtin(Python);Python内置模块的名字空间 #global G —— Global(module);函数定义所在模块(文件)的名字空间
deff1():
#enclosing E —— Enclosing function locals;外部嵌套函数的名字空间(例如closure)
deff2():
#enclosing E —— Enclosing function locals;外部嵌套函数的名字空间(例如closure)
deff3():
#local L —— Local(function);函数内的名字空间
pass
1、内置名称空间
# 存放的名字:存放的是python解释器内置的名字
# 存活周期:python解释器启动则产生,python解释器关闭则销毁
#在Python解释器内:
>>> input
'''
2、全局名称空间
# 存放的名字:只要不是函数内定义、也不是内置的,剩下的都是全局名称空间的名字
# 存活周期:python文件执行则产生,python文件运行完毕后销毁
即 伴随python文件的开始执行/执行完毕而产生/回收,是第二个被加载的名称空间,文件执行过程中产生的名字都会存放于该名称空间中,如下名字:
import sys #模块名sys
x=1 #变量名x
if x == 1:
y=2 #变量名y
def foo(x): #函数名foo
y=1
defbar():passClass Bar:#类名Bar
pass
3、局部名称空间
# 存放的名字:在调用函数时,运行函数体代码过程中产生的函数内的名字;
即 函数的形参、函数内定义的名字都会被存放于局部名称空间中
# 存活周期:在调用函数时存活,函数调用完毕后则销毁
deffunc(a,b):passfunc(10,1)
func(11,12)
func(13,14)
func(15,16)
deffoo(x):
y=3 #调用函数时,才会执行函数代码,名字x和y都存放于该函数的局部名称空间中
4、名称空间加载顺序
名称空间的加载顺序是:内置名称空间->全局名称空间->局部名称空间
5、名称空间销毁顺序
名称空间销毁顺序与名称空间的加载顺序相反,即 局部名称空间>全局名空间>内置名称空间
6、名字的查找优先级
查找一个名字,必须从三个名称空间之一找到,由 当前所在的位置向上一层一层查找
内置名称空间
全局名称空间
局部名称空间
(1)如果当前在局部名称空间:
查找顺序为:局部名称空间->全局名称空间->内置名称空间->最后都没有找到就会抛出异常
#input=333 #全局作用域的名字input
deffunc():
input=444 #局部名字input
print(input) #在局部找input
func()
输出结果:444
input=333 #全局作用域的名字input
deffunc():
#input=444 #局部没有名字input,则找 全局作用域的名字input
print(input)
func()
输出结果: 333
(2)如果当前在全局名称空间
查找顺序为:全局名称空间->内置名称空间->最后都没有找到就会抛出异常
input=333 #全局变量
deffunc():
input=444 #在函数调用时产生局部作用域的名字input
func()print(input) # 该语句不属于函数体,属于全局命名空间;函数调用时,对该语句没有影响,所以直接执行打印功能,从全局命名空间开始找,即输出全局变量 input=333
输出结果:333
#input=333
deffunc():
input=444func()print(input) # 该语句不属于函数体,属于全局命名空间;函数调用时,对该语句没有影响,所以直接执行打印功能,从全局命名空间开始找,
全局空间没有值,则在内置命名空间找,即
输出结果:
栗子1号:
deffunc():print(x)
x=111 #全局变量
func()
栗子2号:名称空间的"嵌套"关系是以函数定义阶段为准,与调用位置无关
x=1
deffunc():print(x)deffoo():
x=222func()
foo()
栗子3号:函数嵌套定义——
python支持函数的嵌套定义,在内嵌的函数内查找名字时,会优先查找自己局部作用域的名字,
然后由内而外一层层查找外部嵌套函数定义的作用域,没有找到,则查找全局作用域
先运行一个函数:
deff2():
input= 333
print(input)
input= 222 # 是全局变量,函数f2调用时先在函数内的局部空间找值,有局部变量input = 333,故打印该值。f2()
输出结果:333
将上述函数作为子函数,函数的嵌套定义:
input=111
deff1():deff2():
input=333
print(input)
input=222f2()
f1() # f1嵌套子函数f2,子函数f2即为f1的函数体,所以函数调用执行过程同上
输出结果:333
input=111
deff1():deff2():#input=333 #将局部变量注释,则f2执行时,在函数的局部空间内找不到值,会在其上一层的函数空间找,存在全局变量 input=222,执行f2,输出结果 222
print(input)
input=222f2()
f1()
输出结果:222
input=111
deff1():deff2():#input=333
print(input)#input=222 #若将f2的局部空间和最接近的全局空间的变量都去除,则函数f2执行时,会继续往下一个全局空间找值,存在input=111,则输出结果 111
f2()
f1()
输出结果:111
栗子4号:先定义,后使用
x=111
deffunc():print(x)
x=222func()#UnboundLocalError: local variable 'x' referenced before assignment#即 变量x在使用前并未定义
修改一下:
x=111
deffunc():
x= 222
print(x)
func()
输出结果:222
二、作用域
作用域,即 作用范围。
1. 全局作用域与局部作用域
全局作用域:位于全局名称空间、内建名称空间中的名字属于全局范围
1、全局存活(除非被删除,否则在整个文件执行过程中存活)
2、全局有效: 被所有函数共享(在任意位置都可以使用)
x=111 #全局变量,全局有效
deffoo():print(x,id(x))defbar():print(x,id(x))
foo()
bar()print(x,id(x))
输出结果:111 140730714743904
111 140730714743904
111 140730714743904
局部作用域: 位于局部名称空间中的名字属于局部范围
1、临时存活:即在函数调用时临时生成,函数调用结束后就释放
2、局部有效: 只能在函数内使用
x=100 #全局作用域的名字x
deffoo():
x=300 #局部作用域的名字x
print(x) #在局部找x
foo()#结果为300
2. 作用域与名字查找的优先级
(可先参照第一部分内容中的——6、名字查找的优先级)
总结如下:
(1)在局部作用域查找名字时,起始位置是局部作用域,所以先查找局部名称空间,没有找到,再去全局作用域查找:先查找全局名称空间,没有找到,再查找内置名称空间,最后都没有找到就会抛出异常
(2)在全局作用域查找名字时,起始位置便是全局作用域,所以先查找全局名称空间,没有找到,再查找内置名称空间,最后都没有找到就会抛出异常
(3)提示:可以调用内建函数locals()和globals()来分别查看局部作用域和全局作用域的名字,查看的结果都是字典格式。
在全局作用域查看到的locals()的结果等于globals()
(4)python支持函数的嵌套定义,在内嵌的函数内查找名字时,会优先查找自己局部作用域的名字,然后由内而外一层层查找外部嵌套函数定义的作用域,没有找到,则查找全局作用域 (栗子4号)
(5)在函数内,无论嵌套多少层,都可以查看到全局作用域的名字,若要在函数内修改全局名称空间中名字的值,当值为不可变类型时,则需要用到global关键字(栗子2号)
(6)对于嵌套多层的函数,使用nonlocal关键字可以将名字声明为来自外部嵌套函数定义的作用域(非全局)(栗子5号)
(7)当实参的值为可变类型时,函数体内对该值的修改将直接反应到原值(栗子3号)
栗子1号:
x=111
deffunc():
x=222func()print(x) #处于全局空间,所以先从全局空间找,存在x=111,输出结果 111
栗子2号:如果在局部想要修改全局的名字对应的值(不可变类型),需要用global,将该变量声明为全局变量
x=111
deffunc():global x #声明x这个名字是全局的名字,不要再造新的名字了
x=222func()print(x)
输出结果:222
栗子3号:当实参的值为可变类型时,函数体内对该值的修改将直接反应到原值
l=[111,222]deffunc():
l.append(333) #l为可变类型,没有必要声明
func()print(l)
输出结果:
[111, 222, 333]
栗子4号:函数嵌套定义
x=1
defouter():
x=2
def inner(): #函数名inner属于outer这一层作用域的名字
x=3
print('inner x:%s' %x)
inner()
print('outer x:%s' %x)
outer()
#结果为
inner x:3outer x:2
栗子5号:nonlocal(了解): 修改函数外层函数包含的名字对应的值(不可变类型)
对于嵌套多层的函数,使用nonlocal关键字可以将名字声明为来自外部嵌套函数定义的作用域(非全局)
nonlocal x会从当前函数的外层函数开始一层层去查找名字x,若是一直到最外层函数都找不到,则会抛出异常。
x=0deff1():
x=11
deff2():
x=22f2()print('f1内的x:',x)
f1() #执行函数f1,先在函数体内的局部空间找值,存在x=11,则输出;内置函数f2里的x=22,只对f2有用。
输出结果:
f1内的x:11
x=0deff1():
x=11
deff2():
nonlocal x #声明 内置函数f2里的x=22,不是内置函数f2里的局部变量,则归属于外层函数f2,x=22会覆盖掉x=11,作为最后的结果输出。 x=22f2() #调用f2(),修改f1作用域中名字x的值print('f1内的x:',x) #在f1作用域查看x
f1()
输出结果:
f1内的x:22