在谈论python命名空间之前,首先介绍一个因为python命名空间引起的坑。
一、问题实例
项目中使用pyqt编写gui,gui在处理文本文件时使用了10进制转16进制的hex函数
代码片段:
from PyQt4.QtCore import *
print hex(10)
期望的输出为0xa,但实际上运行的时候报错
TypeError: hex(QTextStream): argument 1 has unexpected type 'int'
检查了后发现了是PyQt4.QtCore中存在hex函数,导致系统函数hex被重载了。这个问题是python命名空间
与作用域导致的,那么,什么是python的命名空间与作用域呢?
二、命名空间(Namespace)
【定义】
名称到对象的映射。命名空间由一个字典实现,键为变量名,值是变量对应的值。各个命名空间是独立的,
一个命名空间中不能有重名,但是不同的命名空间可以重名而没有任何影响。
【分类】
Python程序执行期间会有2个或3个活动的命名空间(函数调用时有3个,函数调用结束后2个)。按照
变量定义的位置,可以划分为以下3类:
1 、Local。局部命名空间,每个函数所拥有的命名空间,记录了函数中定义的所有变量,包括函数的入参、
内部定义的局部变量。
local命名空间可以在函数中使用locals()函数获取。
def test_function_namespace():
a = 1
b = 2
print locals()
执行结果如下,表示a,b两个变量属于test_function_namespace这个函数的本地命名空间:
{'a': 1, 'b': 2}
2、Global。全局命名空间,每个模块加载执行时创建的,记录了模块中定义的变量,包括模块中定义的函数、
类、其他导入的模块、模块级的变量与常量。
global命名空间可以在模块中任意位置使用globals()函数获取。
def test_module_namespace():
a = 1
b = 2
print globals()
执行结果如下,可以看到test_module_namespace中的a、b变量并不属于该模块的全局命名空间,说明
local与global命名空间是相互隔离的:
{'test_class_namespace': , 'test_module_namespace': , '__builtins__': , '__file__': 'Z:/test/test_celery/tasks/test.py', 'TestClass': , '__package__': None, 'test_instance_namespace': , 'test_function_namespace': , '__name__': '__main__', '__doc__': None}
3、Built-in,python自带的内建命名空间,任何模块均可以访问,放着内置的函数和异常。从某种意义上来
说,一个对象(object)或类(Class)的所有属性(attribute)(包含方法)也构成了一个namespace。在程序执行
期间,有多个类实例就会有多个命名空间同时存在。
类或对象的命名空间用__dict__()方法获取获取类的命名空间def test_class_namespace():
print TestClass.__dict__
执行结果如下,所有TestClass类的对象共享该命名空间:
{'a': 1, '__module__': '__main__', '__dict__': , '__weakref__': , '__doc__': None, '__init__': }
期望的输出为0xa,但实际上运行的时候报错
获取对象的命名空间
def test_instance_namespace():
test_instance = TestClass()
print test_instance.__dict__
执行结果如下,每一个实例都会有一个独立的命名空间:
{'b': 2}
三、作用域(Scope)
【定义】
作用域是针对变量而言,指申明的变量在程序里的可应用范围。或者称为变量的可见性。
【分类】当程序引用某个变量的名字时,就会从当前名字空间开始搜索。搜索顺序规则便是: LEGB。即从内往外一层
一层的查找,找到了之后,便停止搜索,如果最后没有找到,则抛出在NameError的异常
【命名空间与作用域的关系】
命名空间定义了在某个作用域内变量名和绑定值之间的对应关系,命名空间是键值对的集合,变量名与值是
一一对应关系。作用域定义了命名空间中的变量能够在多大范围内起作用。
四、解决方案:
在module_yy中执行from module_xx import func_xx后,module_yy的命名空间中就拥有了func_xx。
后面再更改module_xx 命名空间中的fun_xx,不会改变module_yy的命名空间中的func_xx。
而在module_yy中执行import module_xx 后,module_yy的命名空间中就拥有了module_xx。可以通过
module_xx.func_xx调用函数,后面再更改module_xx 命名空间中的fun_xx,在module_yy的调用
module_xx.func_xx也会改变。
修改后的代码:
import PyQt4.QtCore
print hex(10)
输出为:
0xa
五、疑问:
为何大部分python源代码都使用了from module_xx import func_xx方式?