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

  在谈论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)

扫描二维码关注公众号,回复: 540773 查看本文章

   【定义】

         名称到对象的映射。命名空间由一个字典实现,键为变量名,值是变量对应的值。各个命名空间是独立的,

    一个命名空间中不能有重名,但是不同的命名空间可以重名而没有任何影响。

   【分类】

          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': <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方式?

猜你喜欢

转载自blog.csdn.net/u012474535/article/details/79280521