Python 定义了两种类型的包,常规包和命名空间包。常规包是一个包含多个模块的文件夹,每个模块都有一个对应的 .py 文件;命名空间包是一个包含多个模块的文件夹,但不需要每个模块都有一个对应的 .py 文件。
一、包
在 Python 中,包和模块都是用于组织代码的方式。包是一个包含多个模块的文件夹,而模块是一个包含 Python 代码的文件。包的主要目的是将相关的模块组织在一起,以便更好地管理和使用。所有包都是模块,但并非所有模块都是包,或者换句话说,包只是一种特殊的模块,特别地,任何具有 __path__ 属性的模块都会被当作是包。
包可以通过层次结构进行组织,这意味着一个包可以包含其他包(子包)。这种层次结构使得包可以更有效地组织和管理代码。例如,我们可以创建一个名为 “my_package” 的包,其中包含两个子包 “subpackage1” 和 “subpackage2″。每个子包都可以包含自己的模块。
所有模块都有自己的名字。 子包名与其父包名会以点号分隔,与 Python 的标准属性访问语法一致,因此可能会有一个名为 email 的包,这个包中又有一个名为 email.mime 的子包以及该子包中的名为 email.mime.text 的子包。
二、常规包
常规包是传统的包类型,它们在 Python 3.2 及之前就已存在。 常规包通常以一个包含 __init__.py 文件的目录形式实现, 当一个常规包被导入时,这个 __init__.py 文件会隐式地被执行,它所定义的对象会被绑定到该包命名空间中的名称。__init__.py 文件可以包含与任何其他模块中所包含的 Python 代码相似的代码,Python 将在模块被导入时为其添加额外的属性。
例如,以下文件系统布局定义了一个最高层级的 parent 包和三个子包:
parent/ __init__.py one/ __init__.py two/ __init__.py three/ __init__.py
导入 parent.one 将隐式地执行 parent/__init__.py 和 parent/one/__init__.py。 后续导入 parent.two 或 parent.three 则将分别执行 parent/two/__init__.py 和 parent/three/__init__.py。
三、命名空间包
命名空间包是由多个部分构成的,每个部分为父包增加一个子包。各个部分可能处于文件系统的不同位置,也可能处于 zip 文件中、网络上,或者 Python 在导入期间可以搜索的其他地方。命名空间包并不一定会直接对应到文件系统中的对象;它们有可能是无实体表示的虚拟模块。
命名空间包的 __path__ 属性不使用普通的列表,而是使用定制的可迭代类型。如果其父包的路径(或者最高层级包的 sys.path)发生改变,这种对象会在该包内的下一次导入尝试时自动执行新的对包部分的搜索。
命名空间包没有 parent/__init__.py 文件。实际上,在导入搜索期间可能找到多个 parent 目录,每个都由不同的部分所提供。因此 parent/one 的物理位置不一定与 parent/two 相邻。在这种情况下,Python 将为顶级的 parent 包创建一个命名空间包,无论是它本身还是它的某个子包被导入。