5.2. 包

实际上分析一个XML文档非常简单:只要一行代码。然而,在我们接触那行代码之前,我们需要暂时叉开一下,讲一讲包。

例 5.4. 装入一个XML档(偷偷看一下)

>>> from xml.dom import minidom 1
>>> xmldoc = minidom.parse('/a/diveintopython/common/py/kgp/binary.xml')
1

这个语法我们未曾见过。它看上去很象我们所知并且喜欢的 from module import,但 "." 使得它好象不止是 import 这么简单。事实上,xml 是我们所知道的包,dom 是在 xml 里的嵌套包,而 minidomxml.dom 里的一个模块。

这个看上去挺复杂的,其实不然。看一下确切的实现可能会有帮助。包不过是模块的目录;嵌套包是子目录。一个包中的模块(或一个嵌套包)仍然只是 .py 文件,永远都是,只是它们在一个子目录中,而不是在你的Python安装环境的主 lib/ 目录中。

例 5.5. 包的文件布局

Python21/           Python安装根目录(可执行的根目录)
|
+--lib/             库目录(标准库模块的根目录)
   |
   +-- xml/         xml 包(实际上只是一个有其它东西的目录)
       |
       +--sax/      xml.sax 包(还只是一个目录)
       |
       +--dom/      xml.dom 包(包含 minidom.py)
       |
       +--parsers/  xml.parsers 包(内部使用)
所以当我们说 from xml.dom import minidom,Python会领会到意思是说“在xml 目录中查找 dom 目录,并在 dom 中查找 minidom 模块,接着按 minidom 将其导入”。而且Python甚至比这样做还要聪明,你不仅仅可以将包含在一个包中的整个模块导入,还可以从包含在包中的模块中有选择地导入指定的类或函数。你也可以将包本身当作一个模块导入。语法都是一样的。Python会根据包中文件的布局明白你的意思,自动执行正确的事情。

例 5.6. 包也是模块

>>> from xml.dom import minidom         1
>>> minidom
<module 'xml.dom.minidom' from 'C:\Python21\lib\xml\dom\minidom.pyc'>
>>> minidom.Element
<class xml.dom.minidom.Element at 01095744>
>>> from xml.dom.minidom import Element 2
>>> Element
<class xml.dom.minidom.Element at 01095744>
>>> minidom.Element
<class xml.dom.minidom.Element at 01095744>
>>> from xml import dom                 3
>>> dom
<module 'xml.dom' from 'C:\Python21\lib\xml\dom\__init__.pyc'>
>>> import xml                          4
>>> xml
<module 'xml' from 'C:\Python21\lib\xml\__init__.pyc'>
1

这里我们从一个嵌套包(xml.dom)导入一个模块(minidom)。结果就是 minidom 被导入进我们的名字空间。为了引用 minidom 模块内的类(如 Element),我们必须在它们前面加上模块名。

2

这里我们正在从一个嵌套包(xml.dom)中的模块(minidom)中导入一个类(Element)。结果就是 Element 被直接导入到我们的名字空间中。请注意这样做不会干扰以前的导入, Element 类现在可以用两种方式引用了(但其实都是同一个类)。

3

这里我们正将 dom 包作为一个模块导入。包的任何层次都可以视为一个模块,一会我们就会看到。它甚至可以拥有自已的属性和方法,就象我们在前面看过的模块。

4 这里我们正在将根层次的 xml 包作为一个模块导入。

那么为什么一个包(它不过是磁盘上的一个目录)能够被导入并被看成一个模块(它总是在磁盘上的一个文件)呢?答案就是神奇的 __init__.py 文件。你明白了吧,包不只是目录,它们是带有一个特殊文件, __init__.py,在里面的目录。这个文件定义了包的属性和方法。例如,xml.dom 包含了一个 Node 类,它定义在 xml/dom/__init__.py 中。当你按一个模块方式导入一个包时(象从 xml 中导入 dom),实际上导入了它的 __init__.py 文件。

Note

包是一个其中带有特殊 __init__.py 文件的目录。这个 __init__.py 文件定义了包的属性和方法。它不必定义任何东西,可以只是一个空文件,但它必须存在。但是如果 __init__.py 不存在,目录则只是一个目录,而不是一个包,则它不能被导入或包含模块或包含嵌入包。

那么为什么非得用包呢?嗯,它们提供了将相关的模块在逻辑上归为一组的方法。不使用其中带有 saxdom 包的 xml 包,作者也可以选择将所有的 sax 功能放入 xmlsax.py ,并且将所有的 dom 功能放入 xmldom.py,或者干脆将所有东西放一单个模块中。但这样可能不实用(在写到这儿时候,XML包大约有3000行代码)并且管理也困难(独立源文件意味着多个人可以同时操作不同的地方)。

如果你发现自已正在用Python编写一个大型的子系统(或,很有可能,当你意识到你的小子系统已经成长为一个大型子系统),你应该花费一些时间设计一个好的包架构。它是Python所擅长的事情之一,所以应该好好利用它。