Python中的命名空间介绍 -- 以及nonlocal 和global 的使用

1. 命名空间

Python程序运行时, 会根据需要创建命名空间. 按照命名空间的生命周期来划分, 可将命名空间分为4种

  1. 内置 ( Built-in )
  2. 全局 ( global )
  3. 闭包 ( Enclosing )
  4. 本地 ( local )

1.1 内置命名空间

内置空间中保存着Python内置的变量对象(如 print函数, list对象等 ), 内置命名空间由Python解释器创建和销毁, 只要python程序在运行, 内置空间的变量就是生效的. 如:任何时候都可以调用print函数和使用list创建列表.

内置空间的所有变量保存在变量__builtins__中, 可以使用 dir(__builtins__) 来查看其中的内容

In [1]: dir(__builtins__)
Out[1]:
['ArithmeticError',
 'AssertionError',
 'AttributeError',
 ...
 '__name__',
 '__package__',
 ...
 'print',
 'property',
 'range']

可以看到其中保存了内置的函数和数据类型(print, range, tuple), 内置变量(__name__, __doc__), 和通用的异常(TypeError).

1.2 全局命名空间

全局命名空间包含在主程序中定义的任何名称。Python 在主程序体启动时创建全局命名空间,并且它一直存在直到解释器终止。包含在py文件中定义的和从其他模块import的内容.

举一个例子来更好的理解这个概念, 创建一个namespace.py 文件

from random import randint

VAR = 'global varible'

class Demo:
    def __init__(self):
        pass

def func():
    print('function')

print(globals())

执行python namespace.py 执行print(globals())语句会将当前全局变量打印出来. 结果如下

{'__name__': '__main__', 
 '__doc__': None, 
 '__package__': None, 
 '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x0000024E79813970>, 
 '__spec__': None, 
 '__annotations__': {}, 
 '__builtins__': <module 'builtins' (built-in)>, 
 '__file__': 'c:\\Users\\Documents\\demo\\namespace.py', 
 '__cached__': None, 
 'randint': <bound method Random.randint of <random.Random object at 0x0000024E79A4FF40>>, 
 'VAR': 'global varible', 
 'Demo': <class '__main__.Demo'>, 
 'func': <function func at 0x0000024E79817F70>}

可以看到, 定义的变量VAR, 函数func和类Demo, 以及从random模块import的函数randint被加入到了golbal命名空间中.

1.3 本地和闭包命名空间

每当一个函数被执行时,解释器都会创建一个新的命名空间。该命名空间就是函数的本地命名空间,该命名空间函数返回后被销毁。

如果在函数中再次定义函数, 对于外层函数的本地命名空间就是内层函数的闭包命名空间.

下面通过一个例子来说明

定义一个函数 outter, 接受一个参数 arg, 函数中定义一个变量var和一个函数 inner.  inner函数接受一个参数 a,  函数中定义一个变量var. 

In [1]: def outter(arg):
   ...:     var = 'enclosing'
   ...:     def inner(a):
   ...:         var = 'inner var'
   ...:         print('inner locals: ', locals())
   ...:     inner('inner var')
   ...:     print('outter locals: ', locals())
   ...:

In [2]: outter('param')
inner locals:  {'a': 'inner var', 'var': 'inner var'}
outter locals:  {'arg': 'param', 'var': 'enclosing', 'inner': <function outter.<locals>.inner at 0x00000202B37C8558>}

执行之后可以看到在inner函数中, 本地命名空间locals中包含了变量a 和var.  在outter函数中, 本地命名空间locals中包含变量arg, var 和函数inner. outter函数中的本地命名空间即是inner函数的闭包命名空间.

2. 变量作用域

多个命名空间的结果是, 同一个变量名称可以在多个命名空间中存在, 指向不同的对象, 彼此之间相互不干扰. 当同一变量在不同的命名空间中存在时,  Python遵循如下Local > Enclosing > Global > Built-in 的顺序搜索, 如果最终还是没有找到对应的对象, 抛出NameError异常

下面使用几个实例来展示一下变量的搜索过程, 变量 var 在函数inner, 函数outter和全局作用域中都有定义, 执行函数outter()来运行其中的inner函数后, print语句先在本地作用域中查找var引用的对象, print 出来是 inner var

在函数inner中没有定义 var 的时候,降级到inner函数的闭包作用域 (即outter函数的本地作用域 )去寻找, print 出来的结果是 enclosing

 在函数outter中也没有定义 var 的时候,降级到全局作用域去寻找, print 出来的是全局变量 global

3. 修改外部命名空间的变量

    3.1 可变对象 如:list, dict

     对于可变对象, 在内部作用域中对外部对象进行引用, 对对象的修改会作用到对象本身.

In [1]: array = ['adobe', 'abandon', 'aladdin']
In [2]: def modify():
   ...:     array.append('ariza')

In [3]: modify()

In [4]: array
Out[4]: ['adobe', 'abandon', 'aladdin', 'ariza']

 3.2 不可变对象 如:int, string

正常情况下, 函数修改不了作用域外的不可变对象.

In [1]: var = 100
In [2]: def func():
   ...:     var = 200
   ...:     print(var)

In [3]: func()
200
In [4]: var
Out[4]: 100

 3.3 使用global 和nonlocal

需要修改作用域外的变量, 可以使用 global 声明变量是全局变量.  使用global将var声明为全局变量后, 在函数内func内对var的修改, 将会修改全局变量var的值.

In [1]: var = 100
In [2]: def func():
   ...:     global var
   ...:     var = 200
   ...:     print(var)

In [3]: func()
200
In [4]: var
Out[4]: 200

 nonlocal 的使用方式同理, 区别nonloca将变量声明为修改闭包作用域中的变量. 

In [1]: def outter():
   ...:     var = 'enclosing'
   ...:     def inner():
   ...:         nonlocal var
   ...:         var = 'inner var'
   ...:     inner()
   ...:     print(var)

In [2]: outter()
inner var

 如上代码可见, inner函数中将var声明为nonlocal, 运行inner()函数之后, outter函数中的var被修改成 'inner var'

可以理解为 nonlocal global 将当前作用域外的变量, 拿到了本地作用域使用.

4. 总结

  1. Python的 4 种命名空间, 以及作用域的优先级Local > Enclosing > Global > Built-in
  2. 修改作用域外的数据, 可变对象的修改直接影响本地命名空间外的数据
  3. 修改作用域外的数据, 不可变对象, 使用nonlocal和global声明之后才可以修改作本地命名空间的数据

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值