Python的命名空间解析
2017-11-26 11:54
253 查看
摘要:
什么是命名空间
命名空间有哪些
变量查找原则
分析一个UnboundLocalError的例子
在上面的例子中,我们首先通过
在上面的代码中,我们分别访问了,模块,类,实例,函数的命名空间,并在其中取出了我们命名的变量。其中,访问函数的命名空间有些特殊,我们是通过
LEGB的意思就是
遇到一个名字的时候,Python解释器首先会去本地命名空间(
例如下面的一段代码:
在上面的代码中,s 是
在上面的函数中,我们要打印一个名字
为什么删除了x的赋值语句,这个函数就能正常运行了呢?
这是因为此时Python解释器查找命名空间的顺序变了。解释器首先会去查找本地命名空间,发现没有,然后去查找函数
通过查看这两个函数的反汇编出来的代码可以看到,这两个函数访问x的时候,一个是通过
什么是命名空间
命名空间有哪些
变量查找原则
分析一个UnboundLocalError的例子
什么是命名空间
首先说什么是命名空间呢!我们知道,在Python中,一切都是对象,然后通过名字去引用对象。例如我们执行了一条语句a = 3,Python做的工作就是让名字
a去引用一个 Int 对象
3。这种名字和对象的对应关系都存储在一个字典中,而这个字典,就叫做命名空间。任何一个模块,类,实例,函数,都有其命名空间,这个命名空间可以通过
__dict__来访问。比如下面这段代码:
>>> import sys >>> m = sys.modules[__name__] >>> print(m.__dict__ is globals()) True >>> a = 3 >>> globals().get('a') 32
在上面的例子中,我们首先通过
sys.modules获取了当前运行的模块,然后通过
__dict__成员获取了当前模块的命名空间,然后发现
globals函数返回的就是当前模块的命名空间。当我们执行了
a = 3语句以后,可以看到,当前模块的命名空间中多了一个
a成员,其值为3。
命名空间有哪些
前面我们讲到,每个类,函数,实例,模块都有其命名空间,可以通过__dict__成员来访问,如下面代码所示:
>>> import sys >>> main_module = sys.modules[__name__] >>> module_member = 'module' >>> class C: ... class_member = 'class' >>> c = C() >>> c.instance_member = 'instance' >>> def func(): ... func_member = 'function' ... fls = sys._getframe(0).f_locals ... print('fls and locals() are same object? %s' % (fls is locals())) ... print(fls['func_member']) >>> >>> print(main_module.__dict__['module_member']) module >>> print(C.__dict__['class_member']) class >>> print(c.__dict__['instance_member']) instance >>> func() fls and locals() are same object? True function
在上面的代码中,我们分别访问了,模块,类,实例,函数的命名空间,并在其中取出了我们命名的变量。其中,访问函数的命名空间有些特殊,我们是通过
_getframe函数访问了当前栈帧,然后在栈帧中找到了当前函数的命名空间。当然这个函数的命名空间也可以通过
locals函数访问到。
变量查找原则
从上面的例子中,我们了解到,命名空间其实就是存储名字和对象的对应关系的,那么当我们通过一个名字来获取一个对象的时候,其查找命名空间的顺序是怎样的呢?在Python中遵循着LEGB的查找原则。LEGB的意思就是
Local -> Enclosing Function -> Global -> Builtins。
遇到一个名字的时候,Python解释器首先会去本地命名空间(
Local)中查找,然后再去其所在函数的作用域(
Enclosing Function)中查找,如果还没找到,就去全局命名空间(
Global)中查找,最后会去
__builtin__这个模块中查找,
__builtin__模块在 Python3 中已经重命名成了
builtins,详见 stackoverflow 上关于
builtin的提问,感谢@欧阳杰童鞋指出这个问题。
例如下面的一段代码:
>>> x = 1234 ... __builtin__.s = 'Hello, World!' ... def test(): ... y = 'abc' ... print(x) ... print(y) ... print(s) ... ... test() ... 1234 abc Hello, World!
在上面的代码中,s 是
builtins命名空间中的名字,y 是
local命名空间中的名字,x 是
Global命名空间中的名字,它们都能够被正确找到并打印出来。
分析一个UnboundLocalError例子
在最后,我们来分析这样一段代码:x = 1234 def test(): print(x) x = 'abc' test()
--------------------------------------------------------------------------- UnboundLocalError Traceback (most recent call last) <ipython-input-45-9f45a812fe7a> in <module>() 4 x = 'abc' 5 ----> 6 test() <ipython-input-45-9f45a812fe7a> in test() 1 x = 1234 2 def test(): ----> 3 print(x) 4 x = 'abc' 5 UnboundLocalError: local variable 'x' referenced before assignment
在上面的函数中,我们要打印一个名字
x,它首先会去本地命名空间中查找,没有找到。然后去当前函数
test的作用域中查找,找到了。此时Python解释器就会发现
x这个名字还没有添加到local本地命名空间中,就被引用了。所以就会抛出一个异常,说
x还未被赋值就被引用了。如果我们把代码改成下面这种形式,发现就可以正常运行了:
x = 1234 def test(): print(x) test()
1234
为什么删除了x的赋值语句,这个函数就能正常运行了呢?
这是因为此时Python解释器查找命名空间的顺序变了。解释器首先会去查找本地命名空间,发现没有,然后去查找函数
test的作用域,发现也没有,接着再去查找全局命名空间,此时找到了,就会打印出x的值。
import dis
x = 1234def test_right():
print(x)
dis.dis(test_right)
print('-' * 20)
x = 1234def test_error():
print(x)
x = 'abc'
dis.dis(test_error)
5 0 LOAD_GLOBAL 0 (print) 3 LOAD_GLOBAL 1 (x) 6 CALL_FUNCTION 1 (1 positional, 0 keyword pair) 9 POP_TOP 10 LOAD_CONST 0 (None) 13 RETURN_VALUE -------------------- 13 0 LOAD_GLOBAL 0 (print) 3 LOAD_FAST 0 (x) 6 CALL_FUNCTION 1 (1 positional, 0 keyword pair) 9 POP_TOP 14 10 LOAD_CONST 1 ('abc') 13 STORE_FAST 0 (x) 16 LOAD_CONST 0 (None) 19 RETURN_VALUE
通过查看这两个函数的反汇编出来的代码可以看到,这两个函数访问x的时候,一个是通过
LOAD_GLOBAL指令,访问的全局命名空间,另外一个则是通过
LOAD_FAST指令访问的本地命名空间。通过这些反汇编出来的代码,可以部分地证明我们上面的猜想。
相关文章推荐
- Python中将字典转换为XML以及相关的命名空间解析
- XML的命名空间与python解析方法
- Python中将字典转换为XML以及相关的命名空间解析
- Android布局文件中命名空间的解析
- Python命名空间和作用域窥探
- python-命名空间
- Spring4.3.x 浅析xml配置的解析过程(5)——解析自定义命名空间的标签
- PHP命名空间规则解析及高级功能(1)
- PHP 面向对象编程和设计模式 (5/5) - PHP 命名空间的使用及名称解析规则
- Python作用域和命名空间(Scope and Namespace)
- Python的一个命名空间冲突,关于from-import机制
- python命名空间
- Python 作用域和命名空间
- 【flex中的小问题累积】无法解析 CSS 选择器“ToolTip”中命名空间限定的类型“ToolTip”
- Android布局文件中命名空间的解析
- Python 变量作用域 —— 命名空间与 LEGB 规则
- C++命名空间 namespace的作用和使用解析
- Spring源码阅读-- 解析自定义命名空间的标签
- Python 命名空间和LEGB规则
- Python入门记——模块的命名空间