什么是包?#
一个稍微有点规模的项目,其模块将不止一个,需要某种方法将这些相互关联的模块统一进行管理。对应于使用目录来管理文件,同样可以将多个模块文件所处的目录定义为一个新的东西:包,为了明确标示某个目录是一个包,需要在目录内放入一个 __init__.py 文件。
因此,简单来说,包(Package)是一个包含 __init__.py 文件的目录,是目录内模块的集合。包本身也是一个模块,相对来说,包内的模块又称为子模块(Submodule)。
包的目录结构#
如下面的目录结构中,my_pkg 目录对应一个包,而其中的 foo.py 和 bar.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_pkg1 和 sub_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 * 形式导入时具体导入的内容。