Python Scopes and Namespaces

Python的做用域和命名空间

Namespaces

命名空间是命名到对象的映射.目前大多数命名空间是用Python的字典(dict)实现的,可是具体如何实现可有可无,除非出于性能的考虑,而且未来可能改变其实现方式.例如命名空间是:built-in(builtins模块)的命名的集合,包括例如abs()函数或者built-in异常命名;函数调用的本地命名.在某种意义上对象的属性集也是一个命名空间.关于命名空间须要明白一件很重要的事就是不一样命名空间间绝对没有任何关系;例如,两个模块可能都定义了一个函数maximize而不会混淆--用户必须以模块名为前缀来引用它们.html

命名空间在不一样的时间被建立,并有不一样生命周期.包含built-in命名的命名空间会在GIL启动的时候建立,并且永远不会被删除.对于模块的全局命名空间在模块的定义被读入时建立;一般,模块命名空间也生存到GIL退出前.不管从脚本文件或是交互的语句,在GIL的顶层调用执行的语句被认为是__main__模块的一部分,因此它们有它们本身的全局命名空间.
*某些程序中:if __name__==__main__ 用来判断脚本文件是不是被GIL顶层调用,若是不是能够屏蔽一些没必要要的操做,例如没必要要的提示输出.*python

当函数被调用时它的本地or局部(local)命名空间被建立,当该函数返回(return)或抛出未被函数中处理的异常时命名空间被删除.实际上用遗忘来描述发生了什么更为贴切.若是从新调用改函数,命名空间会从新建立.bash

Scopes

做用域是python程序能够直接访问命名空间的文本区域.这里直接访问是指一个未绑定命名的引用在命名空间试图找到这个命名.闭包

尽管做用域是静态决定的,但都是动态使用的.在任何执行时刻,至少有三种嵌套做用域,其命名空间能够直接访问的:函数

  • 最早搜索的是包含本地命名的最内部做用域
  • 从最近的封闭做用域开始搜索任何封闭函数,包含non-local也包含non-global命名(闭包不熟悉,)
  • 包含当前模块的全局命名
  • 最外层的做用域(最后搜索)是包含built-in命名的命名空间

例子:

def scope_test():
    def do_local():
        spam = "local spam"
        print('dict in do_local()',locals(),'and id',id(spam))

    def do_nonlocal():
        nonlocal spam
        spam = "nonlocal spam"
        print('dict in do_nonlocal()',locals(),'and id',id(spam))

    def do_global():
        global spam
        spam = "global spam"
        print('dict in do_global()',locals(),'and id',id(spam))

    spam = "test spam"

    do_local()
    print("After local assignment:", spam,'and id',id(spam))

    do_nonlocal()
    print("After nonlocal assignment:", spam,'and id',id(spam))

    do_global()
    print("After global assignment:", spam,'and id',id(spam))

    print('dict in scope_test',locals(),'and id',id(spam))

scope_test()
print("In global scope:", spam,'and global dict',globals(),'and id',id(spam))

output:性能

dict in do_local() {'spam': 'local spam'} and id 140120830499184
After local assignment: test spam and id 140120830499440
dict in do_nonlocal() {'spam': 'nonlocal spam'} and id 140120830499248
After nonlocal assignment: nonlocal spam and id 140120830499248
dict in do_global() {} and id 140120830499376
After global assignment: nonlocal spam and id 140120830499248
dict in scope_test {'do_global': <function scope_test.<locals>.do_global at 0x7f706c52e1e0>, 'do_nonlocal': <function scope_test.<locals>.do_nonlocal at 0x7f706c52e158>, 'do_local': <function scope_test.<locals>.do_local at 0x7f706c52e0d0>, 'spam': 'nonlocal spam'} and id 140120830499248
In global scope: global spam and global dict {'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x7f706c5e5080>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': '/home/katachi/PycharmProjects/OtherPrj/pythontest/namespace.py', '__cached__': None, 'scope_test': <function scope_test at 0x7f706c60be18>, 'spam': 'global spam'} and id 140120830499376
  • 关于nonlocal:
    nonlocal语句用来声明一系列的变量,这个声明会从声明处从里到外的namespace去搜寻这个变量(the nearest enclosing scope),直到模块的全局域(不包括全局域),找到了则引用这个命名空间的这个名字和对象,若做赋值操做,则直接改变外层域中的这个名字的绑定。nonlocal列出的命名必须引用封闭范围中的预先存在的绑定
  • 关于locals()和globals()
    分别能够访问本地Namespace和全局Namespace

根据id和locals()的结果,do_local()中的spam属于do_local()的本地命名空间,不能修改scope_test()中的spam.
do_nonlocal()中用关键字nonlocal声明spam,该spam不是新建的,它会向外层寻找,最终绑定scope_test()中的spam.
do_global()中使用关键字global声明spam,可是全局Namespace预先没有spam,则会新建一个spam,这一点经过id()结果也看得出来.ui

简单说,命名空间就是肯定某个命名对应的对象,而做用域则是肯定改命名究竟是哪一个命名空间的.
python肯定某个命名时,有从"内"往"外"的特色,global使得命名能够绑定全局命名空间的对象,而nonlocal则从上一层往外寻找,直到找到或者抛出异常.spa


REF:
https://docs.python.org/3/tutorial/classes.html#python-scopes-and-namespaces
https://docs.python.org/3/reference/simple_stmts.html#nonlocal
http://www.cnblogs.com/livingintruth/p/3296010.htmlrest

相关文章
相关标签/搜索