python的名字空间(namespace)与作用域(scope)

本文介绍了Python中的命名空间概念,包括局部、全局和内置命名空间,并详细解释了作用域的四种类型及其查找顺序。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

名字空间(namespace)

 

1)、名字(name)与对象(object)

对象:对象一块存储区域,用来存储值,同时包含对该值所支持的一系列方法,也包含一系列属性。

名字:每个名字对应一个对象,多个名字可以对应一个对象。这有点类似于别的语言中的别名。

2)、名字空间

名字空间:名字空间是用来专门存储名字与对象的对应关系的地方,在python中名字空间一般使用dict数据结构实现。

在python中,函数、模块等都有自己的命名空间:

局部命名空间(local namespace):即函数中定义的名称 —— 包括函数中的变量、参数、局部变量等;随函数而生,随函数而亡。

全局命名空间(global namespace):即模块中定义的名称 —— 包括模块中的变量、函数、类、参数、常量、导入(import)的模块等;随模块而生,随模块而亡。

内置命名空间(built-in namespace):即python内置的名称 —— 包括各种内置函数、Exception等;随解释器而生,随解释器而亡。

ps:实际上,class也会形成一个特殊的namespace。

而,当python需要使用变量时,会在上述命名空间中依次查找,顺序是:

局部命名空间,全局命名空间、内置命名空间。

同一命名空间中不能有重名,但不同命名空间可以

  • 可以通过locals()(获取局部namespace)、globals()(获取全局namespace) 函数来获取命名空间的值(字典),在程序的不同位置执行结果不一定一致,因为结果是针对当前位置来说的。

ps:因为python是一行一行执行的,当有新的名字与object进行bind的时候,就会将该名字与object的对应关系加入到相应的namespace。

作用域(scope)

作用域:可以理解为变量所起作用的范围,超出范围则某变量不能被使用。在python 程序中,直接访问一个变量,会从内到外依次访问所有的作用域直到找到,否则报错。Python 中只有模块(module),类(class)以及函数(def、lambda)才会产生新的作用域,其它的代码块(如 if/elif/else/、try/except、for/while等)是不会产生新的作用域的。

ps: python使用静态作用域(词法作用域),即标识符的作用域只与它声明的位置有关,一旦声明,则作用域确认。标识符在使用前必须声明,注意函数在被调用前必须声明。

作用域可以分为四种:

Local:最内层,包含局部变量,一般指的是函数内部的作用域;

Enclosing:包含非局部但是也不是全局的变量,主要是嵌套时,外层函数的变量,那么相对内层函数来说,嵌套的外层函数中的变量既不是局部变量也不是全局变量。

Global:全局变量,例如当前模块中的全局变量。

Build-in:内置变量。

查找顺序一般是:Local--->Enclosing--->Global--->Build-in

当查找到一个对应的变量后,就停止继续外层作用域查找。

那么查找的位置就是对应的作用域中的namespace(随着代码的执行,namespace不断改变,添加名字或者删除名字。)。

作用域图:

  • 通过nonlocal可以将一个变量声明为外层作用域变量,即在一个内层作用域将一个name与外层作用域相关联,然后解释器就知道去外层作用域寻找该name对应的那个object了。
  • 通过global可以将一个变量声明为全局作用域变量,即在任何地方将一个name与全局作用域相关联,然后解释器就知道去全局作用域寻找该name对应的那个object了。

#一个典型的例子:

def scope_test():
    def do_local():
        spam = "local spam"    #local scope

    def do_nonlocal():
        nonlocal spam
        spam = "nonlocal spam"  #outer scope,此时改变的scope_test中定义的spam的值。

    def do_global():
        global spam
        spam = "global spam"   #global scope,此时改变的是global scope中定义的spam的值。

    spam = "test spam"
    do_local()  #改变的是local scope中的spam。
    print("After local assignment:", spam)  #scope_test中的spam。
    do_nonlocal() #改变的是scope_test中的spam。
    print("After nonlocal assignment:", spam) #scope_test中的spam。
    do_global()  #改变的是global scope中的spam。 
    print("After global assignment:", spam)  #scope_test中的spam。

scope_test()
print("In global scope:", spam)  #输出global scope中的spam。

运行结果:

After local assignment: test spam
After nonlocal assignment: nonlocal spam
After global assignment: nonlocal spam
In global scope: global spam

#变量寻找的例子:

x=3
def g():
    print(x)  # local scope没有声明x,所以引用到global scope的x.

运行结果:3

x=3
def g():
    x=2   # 定义并赋值本地变量x
    print(x)  # 引用本地变量x

运行结果:2

x=1
def g():
    print(x)
    x=2
g()

运行结果:出错。

python解释器运行python代码都是一行一行读入并运行的,但是对于函数来说,都是一次性读入,所以在上面的代码中,g()是被一次性读入的,并且解释器记住了g()定义了x=2,所以x就会引用到local scope中的变量x,但是x在使用前没有先声明(使用前必须先声明),所以出错。


#函数的两个例子

函数在声明定义阶段不会执行,因此可以使用未定义的名字,但是在运行之前,这些名字必须定义好,否则会出错。

x=1
def f():
    x=3
    g()
    print("f:",x)   # 3


def g():

    print("g:",x)   # 1


f()

print("main:",x)    # 1

这里不会出错,因为在运行之前,我们已经定义了g()。

x=1
def f():
    x=3
    g()
    print("f:",x)


f()   # 报错


def g():

    print("g:",x) 

这里出错,这是因为f()已经运行了,但是g()还没有定义好,所以就出错了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值