python命名空间和作用域的关系_Python:作用域与命名空间

概念

命名空间(namespace):是name到object的映射的集合,在Python中是基于字典实现。

作用域(scope):是Python程序的文本区域。在该区域,某个命名空间中的名字可以被直接引用。

命名空间

当一段代码在Python中执行时,它存在四个命名空间:local,nonlocal,global和built-in。

在执行过程中遇到了某个命名(name)(通俗来说,即Python中的变量)时,Python首先尝试在local命名空间中查找它,如果没有找到,就在nonlocal中查找。之后在global命名空间中查找,如果还是没有找到,接着在built-in命名空间中查找。如果都不存在,则被认为是一个错误,会抛出一个“NameError”异常。[1]

那么,何为local,global,built-in呢?local namespace:比如说一个函数里所有的局部变量。这个命名空间在函数调用时被创建,在函数返回的时候被删除。

nonlocal namespace: 比如外函数中所有的命名。

global namespace: 比如一个模块中定义的所有全局变量。这个命名空间在模块被import的时候创建,在解释器退出时退出。

built-in namespace:包括内置的函数命名(如abs(), str())和异常命名。它在Python解释器启动的时候被创建,在解释器退出的时候才被删除。[2]

作用域

作用域是一个和命名空间紧密结合的概念。如果在python程序中的某一段,这一段中的某一种命名空间(local, global, built-in)中的对象都可以直接访问,那么这个一段区域就被称作一个作用域.[3]

所以,在Python中,说某个变量的作用域是不准确的。准确的说法应该是,在作用域内,该命名空间中的这个对象可以被直接访问到。

在Python程序执行的过程中,至少有以下几个作用域,他们的命名空间(中的对象)可以直接访问:最内层作用域,最先被搜索,包括所有的局部命名。(local names)(Python中不适用变量,因为python中一切皆对象的思想,命名包括各种变量以及函数)

外层函数的局部作用域,包含了外层函数的所有局部命名。(not global, not local)

包含模块中所有全局命名的作用域。

最外层的作用域是包含了内置命名的命名空间。

在两个函数嵌套的时候,对于内部函数,它的局部作用域会引用当前函数内所有的局部命名。而对于外部函数,它的局部作用域会引用的命名空间就是整个模块的命名空间。

global 与 nonlocal

global和nonlocal的具体的作用必须结合Python中作用域理解。通常情况下,对于一个命名到底绑定了哪个对象,是按照从小的作用域到大的作用域的一个查找的过程,但有的时候,我们需要越过中间的层级,直接去上上级的作用域修改命名的绑定。

global:如果一个命名被声明为global,那么对于这个命名的所有的引用与赋值都会作用于模块的全局命名。也就是说,这个命名就会和global命名空间中的相同名字的命名绑定到内存中同一个对象。

nonlocal:nonlocal声明也是用于重新绑定命名,只不过它是用来重新绑定中间层次作用域中的变量。如果你想要在内层函数中使用外层函数中的变量,这个时候只是只读的。如果直接做赋值操作,相当于重新创建了一个相同名称,但作用域不同的命名。而如果使用+=这类的符号更是GG,因为这相当于使用了一个未声明的变量!使用nonlocal声明,就可以直到此时想要重新绑定的是既不是全局命名空间中的命名,也不是局部命名空间中的命名,而是外部函数中的命名。

命名空间与作用域的关系

作用域物理上指的是一段程序区域,在这个区域里的所有命名构成一个命名空间,在这个区域里,这个命名空间包含的所有命名都可以直接访问。

例子

a = 10

def has_local_a():

a = 15

print(locals())

def has_local_a_also():

a = 25

print(locals())

has_local_a()

has_local_a_also()

print(globals().keys())

#--------output------------

{'a': 15}

{'a': 25}

dict_keys(['__name__', '__doc__', '__package__', '__loader__', '__spec__', '__annotations__', '__builtins__', '__file__', '__cached__', 'a', 'has_local_a', 'has_local_a_also']))

在上面,has_local_a和has_local_a_also构成两个局部的作用域,其中的局部命名为a,所以当在这两个方程中使用的时候,解释器首先会在这个局部作用域中找a。如果找不到,就会去引用全局命名空间中的同名的变量。global 与 nonlocal

def test():

def do_local():

spam = "local spam"

def do_nonlocal():

nonlocal spam

spam = "nonlocal spam"

def do_global():

global spam

spam = "global spam"

spam = "test spam"

do_local()

print("after local assignment:", spam) #输出:test spam

do_nonlocal()

print("after nonlocal asssignment:", spam) #输出:nonlocal spam

do_global()

print("after global assignment:", spam) #输出:nonlocal spam

test()

print("in global scope:", spam) #输出:global spa

#--------output------------

after local assignment: test spam

after nonlocal asssignment: nonlocal spam

after global assignment: nonlocal spam

in global scope: global spam

这里例子许多人不理解的地方在于会什么调用do_global()后,spam竟然还是nonlocal spam.原因在于do_nonlocal与do_global中绑定的spam是两个不同的spam,一个spam存在于全局命名空间,一个变量存在于局部的命名空间。而spam是为全局命名spam绑定了一个新的global spam而局部的spam并没有改变。我们可以在do_global()后调用globals()与locals()查看。

{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x000001D1EA0F4F98>, '__spec__': None, '__annotations__': {}, '__builtins__': , '__file__': 'D:/Work/Research/Tutorial/Python/test.py', '__cached__': None, 'test': } # globals

{'do_global': .do_global at 0x000001D1EA365840>, 'do_nonlocal': .do_nonlocal at 0x000001D1EA3657B8>, 'do_local': .do_local at 0x000001D1EA365730>, 'spam': 'nonlocal spam'} # locals

参考文献

[1] Python变量的作用域和命名空间 杨冬

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值