18.python名词空间与作用域

名称空间(namespaces)顾名思义即存放名字与对象映射/绑定关系的地方,是对栈区的划分,在不同的空间中可以存放相同的名字,从而解决命名冲突的问题

18.python名词空间与作用域

作用域指的是名称所对应的有效范围,内置名称空间和全局名称空间在代码全局都能被使用,被称为全局作用域,而局部名称空间在函数内定义,就只能在当前函数使用,就被称为局部作用域


名称空间(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都存放于该函数的局部名称空间中

名称空间的加载销毁顺序

名称空间的加载顺序是:

内置名称空间->全局名称空间->局部名称空间

销毁顺序:

局部名称空间(函数结束销毁)->全局名称空间(文件关闭销毁)->内置名称空间(解释器关闭销毁)

名称空间的查找顺序

18.python名词空间与作用域

首先查找当前所在的空间,如果没有就向上一层一层查找

局部名称空间->全局名称空间->内置名称空间
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
‘’’


上一篇:da'y16_闭包函数


下一篇:斐波那契数列