作者:杨冬 欢迎转载,也请保留这段声明。谢谢!
出处: https://andyyoung01.github.io/ 或 http://andyyoung01.16mb.com/
Python中的 命名空间(namespace) 是标识符到对象的映射,通常以字典的形式表现出来。变量的 作用域(scope) 是Python程序的文本区域,在该区域某个命名空间中的名字可以被直接引用。 赋值(assignment) 操作不会拷贝,只是把标识符和对象做一个绑定。
当一段代码在Python中执行时,它有三个命名空间: local
, global
和 built-in
。在执行过程中遇到了某个标识符时,Python首先尝试在 local
命名空间中查找它,如果没有找到,再在 global
命名空间中查找,如果还是没有找到,接着在 built-in
命名空间中查找。如果都不存在,则被认为是一个错误,会抛出一个“NameError”异常。如图所示:
命名空间都是有创建时间和生存期的。对于Python built-in names组成的命名空间,它在Python解释器启动的时候被创建,在解释器退出的时候才被删除;对于一个Python模块的global namespace,它在这个module被import的时候创建,在解释器退出的时候退出;对于一个函数的local namespace,它在函数每次被调用的时候创建,函数返回的时候被删除。下面看一个例子:
>>> def f(x): print ("global: ",globals()) print ("Entry local: ",locals()) y = x print ("Exit local: ",locals()) >>> z = 2 >>> globals() {'__package__': None, 'f': <function f at 0x0000000000B76EA0>, '__doc__': None, '__name__': '__main__', 'z': 2, '__loader__': <class '_frozen_importlib.BuiltinImporter'>, '__spec__': None, '__builtins__': <module 'builtins' (built-in)>} >>> f(z) global: {'__package__': None, 'f': <function f at 0x0000000000B76EA0>, '__doc__': None, '__name__': '__main__', 'z': 2, '__loader__': <class '_frozen_importlib.BuiltinImporter'>, '__spec__': None, '__builtins__': <module 'builtins' (built-in)>} Entry local: {'x': 2} Exit local: {'y': 2, 'x': 2} >>>
可以看到,刚进入函数f(x)时,只有参数x在f的local命名空间,之后y加入了f的local命名空间。f的global命名空间与我们交互会话的global命名空间相同,因为f是在交互会话时定义的。
再来看一个引入模块的例子,假如有如下的文件:
"""scopetest: our scope test module""" v = 6 def f(x): """f: scope test function""" print("global: ", list(globals().keys())) print("entry local:", locals()) y = x w = v print("exit local:", locals()) |
为了使结果更清晰,下面使用了keys()方法只显示出globals方法返回字典的keys(标识符):
>>> list(globals().keys()) ['__name__', '__loader__', '__doc__', '__spec__', '__package__', '__builtins__'] >>> import scopetest >>> z = 2 >>> list(globals().keys()) ['__name__', '__loader__', '__doc__', 'z', '__spec__', '__package__', 'scopetest', '__builtins__'] >>> scopetest.f(z) global: ['__name__', '__loader__', '__doc__', '__file__', '__spec__', '__package__', 'f', '__builtins__', '__cached__', 'v'] entry local: {'x': 2} exit local: {'y': 2, 'w': 6, 'x': 2} >>>
可见,在交互式会话中,随着模块的import和变量z的定义,交互会话的global命名空间增加了scopetest和z标识符。在scopetest模块的f函数的global命名空间中,包含了方法f和v(但是并没有交互会话中的z)。
[总结]一个模块的引入,函数的调用,类的定义都会引入命名空间;函数中的再定义函数,类中的成员函数定义会在局部namespace中再次引入局部namespace。另外,赋值语句是起一个绑定或重绑定的作用(bind or rebind)。函数调用的参数传递是赋值,不是拷贝。
global语句用来声明一系列变量,这些变量会引用到 当前模块的全局命名空间的变量(module-level namespace) ,如果该变量没有定义,也会在全局空间中添加这个变量。
global var1, var2
nonlocal语句(nonlocal是Python3.2引入的)
Python2.7中还没有nonlocal语句。nonlocal语句用来声明一系列的变量,这个声明会 从声明处从里到外的namespace去搜寻这个变量(the nearest enclosing scope) ,直到模块的全局域(不包括全局域),找到了则引用这个命名空间的这个名字和对象,若作赋值操作,则直接改变外层域中的这个名字的绑定。nonlocal语句声明的变量 不会 在当前scope的namespace字典中加入一个key-value对,如果在外层域中没有找到,则如下报错。
>>> SyntaxError: no binding for nonlocal 'spam' found
一个nonlocal和global的测试例子:
deftest(): defdo_local(): spam = "local spam" defdo_nonlocal(): nonlocal spam spam = "nonlocal spam" defdo_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 spam
第5行的语句:nonlocal spam 没有在函数do_nonlocal()的域中创建一个变量,而是去引用到了外层的,10行定义的spam。
第8行的global spam,在全局域中创建了一个name,9行将其绑定在字符串常量对象”global spam”上。
本文参考了 http://www.cnblogs.com/livingintruth/p/3296010.html 这篇文章及“The Quick Python Book Second Edition”。