名称空间(namespaces)顾名思义即存放名字与对象映射/绑定关系的地方,是对栈区的划分,在不同的空间中可以存放相同的名字,从而解决命名冲突的问题
作用域指的是名称所对应的有效范围,内置名称空间和全局名称空间在代码全局都能被使用,被称为全局作用域,而局部名称空间在函数内定义,就只能在当前函数使用,就被称为局部作用域
名称空间(namespaces)
名称空间分为三种
内置名称空间
存放的名字:python解释器内置的名字
存活周期:python解释器启动则产生,python解释器关闭则销毁
>>> max
<built-in function max> # built-in内建
全局名称空间
存放的名字:只要不是函数内定义并且不是内置的名字剩下的都是全局名称空间
存活周期:python文件执行时产生全局名称空间,运行代码到名字定义位置将名字放入空间,python文件运行完毕后销毁
import sys # 模块名sys
x=1 # 变量名x
if x == 1:
y=2 # 变量名y
def foo(x): # 函数名foo
y=1
def bar():
pass
Class Bar: # 类名Bar
pass
局部名称空间
存放的名字:在调用函数时,运行函数体代码过程中产生的函数内的名字
存活周期:调用函数时存活,函数调用完毕后销毁。
同一个函数调用多遍,调一次产生一个局部名称空间
def foo(x):
y=3 # 调用函数时,才会执行函数代码,名字x和y都存放于该函数的局部名称空间中
名称空间的加载销毁顺序
名称空间的加载顺序是:
内置名称空间->全局名称空间->局部名称空间
销毁顺序:
局部名称空间(函数结束销毁)->全局名称空间(文件关闭销毁)->内置名称空间(解释器关闭销毁)
名称空间的查找顺序
首先查找当前所在的空间,如果没有就向上一层一层查找
局部名称空间->全局名称空间->内置名称空间
print(input,int) # <built-in function input> <class 'int'>
# input和int本应属于内置名称空间,但由于自行定义所以全局名称空间也有了
def func():
input = 444
print(input) # 444 站在局部名称空间,局部空间有则使用局部
print(int) # 111 局部空间没有,则查找全局空间
input = 333
int = 111
print(input) # 333 站在全局名称空间
func() # 定义只是开辟空间,调用时input和int已经被自行定义了
名称空间的’嵌套关系’
实例1
x = 1
def func():
print(x)
def foo():
x = 2
func()
foo() # 1
调用无法导致名称空间的嵌套关系,在当前局部名称空间找不到,就会直接去找全局名称空间
实例2
# f2的名称空间在f1里面
def f1():
x = 111
def f2(): # f2找不到就往上一层f1进行查找
print(x)
f2()
f1() # 111
函数的嵌套是在定义阶段决定的,即检测语法的时候确定的
实例3
x = 111
def func():
print(x)
x = 222
func() # 报错:local variable 'x' referenced before assignment
结果报错
上文提到,函数的嵌套是在函数定义阶段决定的,所以函数定义时func()的局部名称空间知道有x,而定义时只检查代码而不运行代码,而调用时就不会再往上一层查找,程序运行到print(x),局部空间内还没有定义x,所以报错
名称空间的约定沟通方式
由于名称空间存在嵌套的可能,所以某些程序员前辈就巧妙地对嵌套名词空间进行了LEGB命名(非语法,只是约定俗成)
# b Builtin 内置名称空间
# g global 全局名称空间
def f1():
# e enclosing_function_locals 外部嵌套函数名称空间
def f2():
# e enclosing_function_locals 外部嵌套函数名称空间
def f3():
pass # l local 函数内本地名称空间
作用域
作用域简单理解就是作用范围,他其实就是把名称空间归位了两大类
全局作用域
全局作用域:位于全局名称空间、内建名称空间中的名字属于全局范围,该范围内的名字全局存活(除非被删除,否则在整个文件执行过程中存活)、全局有效(在任意位置都可以使用)
局部作用域
局部作用域:位于局部名称空间中的名字属于局部范围。该范围内的名字临时存活(即在函数调用时临时生成,函数调用结束后就释放)、局部有效(只能在所在函数内使用
global函数内定义全局变量
x = 111
y = 222
z = [111,222]
def func():
global y # y是全局变量
x = 333
y = 444
z.append(333) # z是可变类型,修改z内值不会导致z对应新的内存地址,函数内可以直接修改全局列表
func()
print('x={},y={}'.format(x,y)) # x=111,y=444
print(z) # [111, 222, 333]
在函数体内通过global定义的变量不再是函数体的局部变量,而是全局变量
nonlocal修改外层函数名字对应的值
x = 111
def f1():
x = 222
def f2():
x = 333
def f3():
nonlocal x
x = 444
f3()
print('f2的x = {}'.format(x))
f2()
print('f1的x = {}'.format(x))
f1()
print('全局变量的x = {}'.format(x))
'''
f2的x = 444
f1的x = 222
全局变量的x = 111
f3修改了f2的x
'''
nonlocal以当前层向外层寻找x,找到了即进行修改,如果函数内都没有则报错(永远不会修改全局函数)
x = 111
def f1():
x = 222
def f2():
def f3():
nonlocal x
x = 444
f3()
f2()
print('f1的x = {}'.format(x))
f1()
print('全局变量的x = {}'.format(x))
'''
f1的x = 444
全局变量的x = 111
由于f2没有x,所以,f3的nonlocal直接修改到了f1
'''
def f2():
def f3():
nonlocal x
x = 444
f3()
f2()
print(‘f1的x = {}’.format(x))
f1()
print(‘全局变量的x = {}’.format(x))
‘’’
f1的x = 444
全局变量的x = 111
由于f2没有x,所以,f3的nonlocal直接修改到了f1
‘’’