Python的包导入机制

发布时间:2019-09-10 08:55:29编辑:auto阅读(1545)

    Background

    在Python的大型项目中,一般都会用到模块包来组织文件层次,其中当一个目录内含有__init__ . py文件时,就可以視该目录为一个模块包。
    当在模块包中使用import语句的时候,不同的语法会导致不同的模块搜索导入方式,常见的导入方式如下:

    • 绝对导入(absolute import)
    • 显式相对导入(explicit relative import)
    • 隐式相对导入(implicit relative import)

    需要注意的是,这些导入方式都是对于模块包而言,对于一般的模块还是从sys.path搜索入手。

    Prerequisite

    • Python脚本运行的方式?

      • Python脚本运行分为两种方式:一种是作为top level script运行,另一种则是作为被导入的包模块运行。
      • 当使用python命令直接执行一个py文件的时候,该文件就是以top level script方式运行,此时文件的__name__属性则为__main__。

        
        # test.py
        
        print __name__
        
        # output
        
        __main__
        
      • 当文件使用包模块的方式运行的时候,文件的__name__属性则为模块的路径(从top level script的目录开始),包模块的例子目录结构如下:

        ├── main.py
        └── pac
          ├── __init__.py
          └── moduleA.py
        

        笔者将直接运行main.py文件,代码如下:

        
        ## main.py
        
        from pac import moduleA
        
        
        ## moduleA.py
        
        print __name__
        
        
        ## output
        
        pac.moduleA
      • 这两种运行方式还有不同的地方是,当使用top level script的方式运行的时候是不会生成字节码的(即.pyc文件),而通过包模块的方式则会生成字节码。

    Relative Import And Absolute Import

    假设如下的import语句:

    import string

    这个string是当前目录下的string模块呢,还是在标准库的string模块呢?在早期的Python中,当使用import语句的时候,都会优先寻找目录内的模块,因此这就是隐式相对导入。

    但是在有同名模块的情况下,如果还想引用标准库中的string模块那该怎么办?因此Python实现了绝对导入,在绝对导入的模式下,当使用import string的时候,就会优先搜索当前目录以外的模块。绝对导入模式是Python3默认采取的包导入方式,其实这种方式在Python2.5及以上版本就已经实现,要想使用只需加上:

    from __future__ import absolute_import

    关于隐式相对导入于绝对导入的例子如下:
    包结构:

    ├── main.py
    └── pac
        ├── __init__.py
        ├── __init__.pyc
        ├── explicit_import.py
        ├── explicit_import.pyc
        ├── implicit_import.py
        ├── implicit_import.pyc
        ├── string.py
        └── string.pyc

    代码如下:

    # main.py
    from pac import implicit_import
    from pac import explicit_import
    
    # explicit_import.py
    from __future__ import absolute_import
    
    import string
    print string.digits
    
    # implicit_import.py
    import string
    print string.digits
    
    # string.py
    digits = '2333'
    
    ## output
    2333(relative import)
    0123456789(absolute import)

    绝对导入还有一种使用方法,比如在explicit_import.py中可以通过:

    from pac.implicit_import import *

    来引用implicit_import文件中的变量。

    explicit relative import

    虽然绝对导入能够完成相对导入的所有功能,但是显式的相对导入也是可以接受的。当使用.语法的时候就是使用相对导入:

    # 导入当前目录下的string模块
    # right
    from . import string
    # wrong
    import .string 

    至于下面的导入方法错误的原因,这是因为Python语法不支持的缘故。
    同时值得注意的是,显式的相对导入是根据模块的__name__属性来确定相对位置的,因此如果是在top level script中,显式相对导入并不能使用,会报出如下错误:

    ValueError: Attempted relative import in non-package

    当然,在PEP 366 – Main module explicit relative imports中,也给出了在Python中执行非包内的模块(作为top level脚本执行)使用显示相对导入的方法:在执行python命令时加上-m选项,此时就会启用模块的__package__属性。

    详细的关于相对导入与绝对导入参考:PEP 328 – Imports: Multi-Line and Absolute/Relative

关键字

上一篇: Python 模拟Ajax/XMLHtt

下一篇: Python 月份加减