什么是包?#

一个稍微有点规模的项目,其模块将不止一个,需要某种方法将这些相互关联的模块统一进行管理。对应于使用目录来管理文件,同样可以将多个模块文件所处的目录定义为一个新的东西:包,为了明确标示某个目录是一个包,需要在目录内放入一个 __init__.py 文件。

因此,简单来说,(Package)是一个包含 __init__.py 文件的目录,是目录内模块的集合。包本身也是一个模块,相对来说,包内的模块又称为子模块(Submodule)。

包的目录结构#

如下面的目录结构中,my_pkg 目录对应一个包,而其中的 foo.pybar.py 都对应一个子模块。

    my_pkg/
    ├── __init__.py
    ├── bar.py
    └── foo.py

注意包目录下必须有一个 __init__.py 文件,否则 Python 会把该目录当成普通目录而不是包了。最简单的情况下,该文件是一个空文件。该包的名称是 my_pkg(注意不是 __init__)。

进一步地,可以有多级目录,组成多级层次的包结构,其中的子目录也可以被称作是子包(Subpackage)。如下所示:

    my_pkg/
    ├── __init__.py
    ├── bar.py
    ├── foo.py
    ├── sub_pkg1
    │   ├── __init__.py
    │   ├── bar.py
    │   └── foo.py
    └── sub_pkg2
        ├── __init__.py
        ├── bar.py
        └── foo.py

以上的包 my_pkg 中又包含两个子包:sub_pkg1sub_pkg2

导入包中的名称#

当存在多层的树形结构后,就需要用多个点号来逐层表示包中的一些名称,如 my_pkg.sub_pkg2.foo.name,具体使用方法取决于如何导入这些名称。外部代码可以通过如下方式导入包内定义的名称(其中方括号内的内容是可选的):


    import package_name[.module_name [as alias]]
    from package_name import module_name [as alias]
    from package_name.module_name import name [as alias]

总之,包、包中模块、模块中的名称的导入方法基本上是一致的,可以灵活地导入这些名称,并为导入的名称指定别名。

为了便于说明如何导入包,假设我们项目的目录结构为:

    my_project
    ├── main.py
    └── my_pkg
        ├── __init__.py
        ├── bar.py
        ├── foo.py
        ├── sub_pkg1
        │   ├── __init__.py
        │   ├── bar.py
        │   └── foo.py
        └── sub_pkg2
            ├── __init__.py
            ├── bar.py
            └── foo.py

main.py 对应项目的主模块。

当使用 import package_name.module_name 的形式导入包时,引用时必须使用模块的全名。假如 my_pkg.sub_pkg1.foo 模块中有一个函数 do_sth(),则在 my_project/main.py 中可以按如下方法导入模块并使用此函数:


    import my_pkg.sub_pkg1.foo
    my_pkg.sub_pkg1.foo.do_sth()  # 不能使用 foo.do_sth() 的形式

但当以 from package_name import module_name 形式导入时,引用时不必使用全名。如下所示:


    from my_pkg.sub_pkg1 import foo
    foo.do_sth()  # 不能使用 my_pkg.sub_pkg1.foo.do_sth() 的形式

编辑 __init__.py 文件#

前面的各种包的 __init__.py 都是空文件。实际上,可以在该文件中放入包的初始化代码,当包本身或其所包含内容被导入时会执行其中的代码。不过一般会在该文件中定义一个 __all__ 变量,它是一个字符串列表,列表中的每个元素是该包中包含的成员(模块、子包)名称。这样当在其他文件中用 from package_name import * 的形式导入该包的内容时,将只导入该列表中的成员。例如,假如 my_pkg/__init__.py 的内容如下:


    __all__ = ['foo', 'bar']

则可以如下方式编写 my_project/main.py


    from my_pkg import *
    foo.do_sth()  # 在上一行通过 * 导入的名称中已包含 foo

如果不想让他人以 from package_name import * 的形式导入包中特定的子模块,保持 __init__.py 为空文件即可。

实际上,也可以为其他不属于包的模块显式指定 __all__ 变量,其列表中各项可以是变量、函数、类名称等定义项,从而控制以 from module_name import * 形式导入时具体导入的内容。

模块包管理工具