您的位置:首页 > 编程语言 > Python开发

从Python的两种导入模块(from import和import)方式谈命名空间与作用域

2018-02-07 15:43 666 查看
  在谈论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中的
4000
a、b变量并不属于该模块的全局命名空间,说明
local与global命名空间是相互隔离的:{'test_class_namespace': <function test_class_namespace at 0x02ABFDF0>, 'test_module_namespace': <function test_module_namespace at 0x02ABFDB0>, '__builtins__': <module '__builtin__' (built-in)>, '__file__': 'Z:/test/test_celery/tasks/test.py', 'TestClass': <class '__main__.TestClass'>, '__package__': None, 'test_instance_namespace': <function test_instance_namespace at 0x02ABFEF0>, 'test_function_namespace': <function test_function_namespace at 0x02A7AAB0>, '__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__': <attribute '__dict__' of 'TestClass' objects>, '__weakref__': <attribute '__weakref__' of 'TestClass' objects>, '__doc__': None, '__init__': <function __init__ at 0x0299FFB0>}期望的输出为0xa,但实际上运行的时候报错
获取对象的命名空间
def test_instance_namespace():
test_instance = TestClass()
print test_instance.__dict__ 执行结果如下,每一个实例都会有一个独立的命名空间:
{'b': 2}

三、作用域(Scope)
【定义】

    作用域是针对变量而言,指申明的变量在程序里的可应用范围。或者称为变量的可见性。
【分类】

     Local(函数内部)局部作用域

     Enclosing(嵌套函数的外层函数内部)嵌套作用域(闭包)

     Global(模块全局)全局作用域

     Built-in(内建)内建作用域

【变量查找法则】

     当程序引用某个变量的名字时,就会从当前名字空间开始搜索。搜索顺序规则便是: 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方式?
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息