12.7 包

包是一个有层次的文件目录结构,它定义了一个由模块和子包组成的Python应用程序执行环境。Python 1. 5加入了包,用来帮助解决如下问题:

  • 为平坦的名称空间加入有层次的组织结构;

  • 允许程序员把有联系的模块组合到一起;

  • 允许分发者使用目录结构而不是一大堆混乱的文件;

  • 帮助解决有冲突的模块名称。

与类和模块相同,包也使用句点属性标识来访问他们的元素。使用标准的import和from-import语句导入包中的模块。

12.7.1 目录结构

假定我们的包的例子有如下的目录结构:

12.7 包 - 图1

Phone是最顶层的包,Voicedta等是它的子包。我们可以这样导入子包:

12.7 包 - 图2

你也可使用from-import实现不同需求的导入。

第一种方法是只导入顶层的子包,然后使用属性/点操作符向下引用子包树:

12.7 包 - 图3

此外,我们可以还引用更多的子包:

12.7 包 - 图4

事实上,你可以一直沿子包的树状结构导入:

12.7 包 - 图5

在我们上边的目录结构中,我们可以发现很多的init.py文件。这些是初始化模块,from-import语句导入子包时需要用到它。如果没有用到,他们可以是空文件。程序员经常忘记为它们的包目录加入init. py文件,所以从Python 2. 5开始,这将会导致一个ImportWarning信息。

不过,除非给解释器传递了-Wd选项,否则它会被简单地忽略。

12.7.2 使用from-import导入包

包同样支持from-import all语句:

12.7 包 - 图6

然而,这样的语句会导入哪些文件取决于操作系统的文件系统。所以我们在init.py中加入all变量。该变量包含执行这样的语句时应该导入的模块的名字,它由一个模块名字符串列表组成。

12.7.2.1 绝对导入

包的使用越来越广泛,很多情况下导入子包会导致和真正的标准库模块发生(事实上是它们的名字)冲突。包模块会把名字相同的标准库模块隐藏掉,因为它首先在包内执行相对导入,隐藏掉标准库模块。

为此,所有的导入现在都被认为是绝对的,也就是说这些名字必须通过Python路径(sys. path或是PYTHONPATH)来访问。

这个决定的基本原理是子包也可以通过sys. path访问,例如import Phone. Mobile. Analog。在这个变化之前,从Mobile子包内模块中导入Analog是合理的。作为一个折中方案,Python允许通过在模块或包名称前置句点实现相对导入。更多信息请参阅第12.7.4节。

从Python 2. 7开始,绝对导入特性将成为默认功能(从Python 2. 5开始,你可以从future导入absolute_import,体验这个功能)。你可以参阅PEP 328了解更多相关内容。

12.7.2.2 相对导入

如前所述,绝对导入特性限制了模块作者的一些特权。失去了import语句的自由,必须有新的特性来满足程序员的需求。这时候,我们有了相对导入。相对导入特性稍微地改变了import语法,让程序员告诉导入者在子包的哪里查找某个模块。因为import语句总是绝对导入的,所以相对导入只应用于from-import语句。

语法的第一部分是一个句点,指示一个相对的导入操作。之后的其他附加句点代表当前from起始查找位置后的一个级别。

我们再来看看上边的例子。在Analog. Mobile. Digital,也就是Digital.py模块中,我们不能简单地使用这样的语法。下边的代码只能工作在旧版本的Python下,在新的版本中它会导致一个警告,或者干脆不能工作:

12.7 包 - 图7

这是绝对导入的限制造成的。你需要在使用绝对导入或是相对导入中做出选择。下边是一些可行的导入方法:

12.7 包 - 图8

12.7 包 - 图9

从2.5版开始,相对导入被加入到了Python中。在Python 2.6中,在模块内部的导入如果没有使用相对导入,那么会显示一个警告信息。你可以在PEP 328的文档中获得更多相关信息。