Python学习_基础_12_名称空间与作用域

Python-名称空间与作用域

一、名称空间(namespaces)

名称空间namespacs:字面意思看,是存放名字的地方。

我们知道,变量名是存放在栈区的,而值是存放在堆区的。准确的说,栈区存放的是变量名与其对应的值的内存地址的映射/绑定关系。(如下图所示)

所谓名称空间,就是对栈区的划分,可以理解为对栈区里这么多映射/绑定关系划了划片,归了归类。

有了名称空间之后,就可以在栈区中存放相同的名字了,在程序执行期间最多会存在三种名称空间。
  1. 内置名称空间(只有一个)

    伴随python解释器的启动/关闭而产生/回收,因而是第一个被加载的名称空间,用来存放一些内置的名字,比如内置函数名。

    存放的名字:存放的是python解释器内置的名字

    '''
    >>> print
    <built-in function print>
    >>> input
    <built-in function input>
    '''
    

    存活周期:python解释器启动则产生,python解释器关闭则销毁

  2. 全局名称空间(只有一个)

    伴随python文件的开始执行/执行完毕而产生/回收,是第二个被加载的名称空间,文件执行过程中产生的名字都会存放于该名称空间中。

    存放的名字:只要不是在函数内定义的,也不是python内置的,剩下的都是全局名称空间的名字

    存活周期:python文件开始运行则产生,python文件运行完毕后销毁

  3. 局部名称空间(可以有多个)

    伴随函数的调用/结束而临时产生/回收,函数的形参、函数内定义的名字都会被存放于该名称空间中。

    存放的名字:在调用函数时,运行函数体代码过程中产生的函数内的名字

    存活周期:在调用函数时存活,函数调用完毕后则销毁

名称空间的加载顺序:内置名称空间>全局名称空间>局部名称空间
名称空间的销毁顺序:局部名称空间>全局名空间>内置名称空间
名字的查找优先级:从当前所在的位置(空间)向上一级一级查找。
  1. 如果当前在局部名称空间:局部名称空间—>全局名称空间->内置名称空间

    def fun():
        print(x)
    
    
    x = "我在全局名称空间"
    fun()  # 我在全局名称空间
    

    代码像上面这样写居然没有报错,我们以为x没有在函数内被定义,直接输出就会报错,但事实上是在局部名称空间里没找着x,去了全局名称空间找到了。

    x = '我在全局名称空间'
    
    
    def func():
        print(x)
    
    
    def foo():
        x = '我在局部名称空间,归属于foo函数'
        func()
    
    
    foo()  # 我在全局名称空间
    

    名称空间的"嵌套"关系是以函数定义阶段为准,与调用位置无关。也就是说,按照上面的代码,func函数在foo函数里被调用,此时按照惯性思维,我们认为func函数里的x会去foo函数内部找,其实不是这样的。上面代码里表现的这种嵌套关系只是语法上的嵌套,而名称空间的嵌套关系是在函数定义完成的那一刻就确定的,所以func函数内部的局部名称空间的上一层级是全局名称空间。

  2. 如果当前在全局名称空间:全局名称空间->内置名称空间

    print(id(print()))  # 140711167694976
    
名字的查找顺序是以定义阶段为准的,从当前位置开始往外一层一层找,跟在哪调用完全无关。(函数多层级嵌套也是,从所在嵌套层级一层一层往外找)

二、作用域(作用范围)

根据三种不同的名称空间,我们划分了两个作用域:
  1. 全局作用域:位于全局名称空间、内置名称空间中的名字属于全局作用域(全局作用范围),该范围内的名字全局存活(除非被删除,否则在整个文件执行过程中都存活)、全局有效(在任意位置都可以使用,被所有函数共享)。
  2. 局部作用域:位于局部名称空间中的名字属于局部作用域(局部作用范围)。该范围内的名字临时存活(即在函数调用时临时生成,函数调用结束后就释放)、局部有效(只能在函数内使用)。

Python支持函数的嵌套定义,在内嵌的函数内查找名字时,会优先查找自己局部作用域的名字,然后由内而外一层层查找外部嵌套函数定义的作用域,若都没有找到,则查找全局作用域。

三、global与nonlocal

x = '我是全局的x'


def func():
    x = '我是局部的x'


func()
print(x)  # 我是全局的x

如果在局部想要修改全局的名字对应的值(不可变类型),需要用global。

x = '我是全局的x'


def func():
    global x  # 声明x这个名字是全局的名字
    x = '我是局部的x'  # 这里直接改变的是全局变量x,如果全局有则直接改变,如果没有则创建


func()
print(x)  # 我是局部的x

如果在局部想要修改全局的名字对应的值是可变类型,则可以直接改变。

l = [111, 222]


def func():
    l[0] = 100


func()
print(l)  # [100, 222]

要明确注意:通过global改变的是全局的名字对应的值。

x = 0

def f1():
    x = 11
    print('f2执行前f1内的x:', x)
    
    def f2():
        global x
        x = 22
        print('f2里的x:',x)
        
    f2()
    print('f2执行后f1内的x:', x)

print('f1执行前全局的x:', x)
f1()
print('f1执行后全局的x:', x)

# 程序运行结果:
# f1执行前全局的x: 0
# f2执行前f1内的x: 11
# f2里的x: 22
# f2执行后f1内的x: 11
# f1执行后全局的x: 22

在上面函数嵌套中,f2函数里改变了全局x的值,但f1里的x是不受影响的。如果我们想在f2函数中修改f1函数里的x,该怎么做呢?

nonlocal(了解): 修改函数外层函数包含的名字对应的值(不可变类型

x=0
def f1():
    x=11
    def f2():
        nonlocal x
        x=22
    f2()
    print('f1内的x:',x)


f1()  # f1内的x: 22

nonlocal是从当前层函数的外一层开始找,一层一层嵌套函数找下去,当在最后一层函数里仍没找到则会报错:

x=0
def f1():
    def f2():
        nonlocal x
        x=22
    f2()
    print('f1内的x:',x)


f1()  # 程序报错

如果针对的位置是可变类型则可以直接改变:

def f1():
    x=[]
    def f2():
        x.append(1111)
    f2()
    print('f1内的x:',x)


f1()  # f1内的x: [1111]

四、重点

  • 名称空间是对栈的一种划分,真正存在的是栈区,名称空间只是一种虚拟的划分。
  • 名词查找:当前所在的位置向外查找(局部名称空间-》全局名称空间-》内置名称空间)
  • 名称空间只有优先级之分,本身并无嵌套关系,说嵌套只是为了便于理解。
  • 名称空间的嵌套关系决定了名字的查找顺序,而名称空间的嵌套关系是以函数定义阶段为准的,即函数的嵌套关系与名字的查找顺序是在定义阶段就已经确定好的。
  • 全局作用域:内置名称空间+全局名称空间的名字
    全局存活,全局有效

  • 局部作用域:局部名称空间的名字
    临时存活,局部有效

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值