Python3.2官方文档教程--包


4.4 包

包是一种通过“点模块名称”来创建模块命名空间的一种方法。例如,模块A.B表示在A的包下设计了一个子包B。就像模块的应用保存不同模块的作者,这些不同的模块需要考虑相互的全局变量名称。但是点模块名称让多个模块包的作者无需担心彼此的模块名称(冲突),就像Numpy或python图像库。

假设你为了统一操作音频文件和音频数据,你可以定义一个多模块(包)的集合。在里面可能有不同音频文件的格式(通常用它们的扩展名来区别,例如.wav, .aiff, .au)因此你需要创建和维护很多的模块结合来实现不同文件格式之间的转换。也有可能你针对不同的文件格式有不同的操作(例如混音,添加回声,应用均衡器,创建人造立体声等等)。因此,为了进行这些操作你还需要另外编写一个永远也玩不成的模块。这里是你的包的一种可能的结构。

sound/ Top-level package 最顶层文件

__init__.py Initialize the sound package 初始化音频包

formats/ Subpackage for file format conversions 文件格式转换的子包

__init__.py

wavread.py

wavwrite.py

aiffread.py

aiffwrite.py

6.4. Packages 43

Python Tutorial, Release 3.2.3

auread.py

auwrite.py

...

effects/ Subpackage for sound effects 声音效果的子包

__init__.py

echo.py

surround.py

reverse.py

...

filters/ Subpackage for filters 过滤器的子包

__init__.py

equalizer.py

vocoder.py

karaoke.py

...

当导入包后,python会通过sys.path的目录寻找包的子路径。

_init_.py文件将被要求让python认为目录中包含包。这是为了避免一个含有烂俗名字的目录无意中隐藏了稍后在模块搜索路径中出现的有限模块,比如string,最简单的情况下,只需要一个_init_.py文件即可。当然它为包执行初始化代码或者设置_all_变量。

用户每次只能从包中特定的导入模块。例如:

import sound.effects.echo

这种方法加载子模块sound.effects.echo. 必须用全名来引用。

sound.effects.echo.echofilter(input, output, delay=0.7, atten=4)

导入子模块的另一种方式是:

from sound.effects import echo

这种方式也可以加载模块echo,并且没有包的前缀也可以使用,因此它可以如下应用:

echo.echofilter(input, output, delay=0.7, atten=4)

另一种直接导入期望的方法和变量方法:

from sound.effects.echo import echofilter

同时这种方法加载了子模块echo,但是这种方法可以直接调用函数echofilter();

echofilter(input, output, delay=0.7, atten=4)

注意使用from package impport item这种方法时, the item既可是一个子模块或者是在包中定义的其他名称,像函数,类,或者变量。Import语句首先就会检测是否定义在包中。如果没有,它就会假设是个模块然后去加载。如果没有找到,就会报ImportError异常。

相反地,当用表达式import item.subitem.subsubitem,除了最后一个子项其他都是包。最后一个可以使一个模块或者包,但不允许是在定义在模块中的类或者函数或者变量。

6.4.1 从包中导入*

当用户写from sound.effects import * 会发生什么? 理论上,他预期以某种方式友好地对待文件系统,寻找在包下的所有子模块,并且把它们都导入进来。它可能会花费更多的时间,导入子模块可能不希望出现的负面影响,这种负面影响仅仅在子模块被明确导入时候才会出现的。

对包作者而来,唯一的解决办法就是提供了一个关于包的准确索引。 Import语句会用如下的转换。如果一个包的_init_.py代码定义了一个名为_all_的列表,当使用form package import * 时它被当做应该被导入的模块名称的列表。 当发布包的一个新版本时,应该由 包作者负责这个列表时最新的。如果包作者在包中没有发现导入* 的用法。那么他们可以决定不去做这些。例如:

Sound/effects/_init_.py文件有如下代码:

__all__ = ["echo", "surround", "reverse"]

这将意外着from sound.effects. Import * 将会导入在sound包下三个名为显示的子模块。

如果_all_没有定义, 语句form sound.effects import * 不会从包sound.effects导入所有子包到当前的命名空间。它仅仅确保包sound.effects 已经导入。然后导入包中定义的任何名称。

这会引入_init_.py文件中定义的所有名称(及明确加载的模块)。它同样会引入通过之前导入语句明确加载额的包中的所有模块,思考如下代码:

import sound.effects.echo

import sound.effects.surround

from sound.effects import*

在这个例子中,echo和surround模块也会导入到当前命名空间下,因为当from...import执行时他们也定义在sound.effects包中。(这也可以当_all定义时实现)

注意: 通常从包或者模块中导入* 的习惯是不被推荐的,因为他经常会让代码难以阅读,不过,在交互会话中使用它来减少输入是不会有问题的,并且有些模块被设计只能通过特性方法导入。

记住: from package import specific_submodule 永远那是不会错的。事实上,这是被推荐使用的方法,除非在不同包中导入的模块使用了相同的名字。

4.4.2 包内引用

当包设计成好几个子包时(就像在例子中的sound包),你可用绝对导入来指定到子模块或者相应的包。例如,如果模块sound.filters.vocoder 需要用在sound.effects 包中echo模块,它可用from sound.effects import echo.

你也可以写用from module import name 形式导入语句写相关导入。这些导入用.. 来描述涉及到相关导入的当前路径和父包。例如,从surrond模块导入:

from . import echo

from .. import formats

from ..filters import equalizer

注意:相对导入是以当前模块名称为基础的,因为主模块的名称总是_main_,所以以python程序中打算用作主模块的模块必须使用绝对导入。

4.4.3 跨目录的包

包支持一个特殊熟悉,_path_。这会在那个文件执行之前,初始化为一个包含包中_init_路径下的名称列表。这个变量可以修改用来影响对包中包含的子包和模块的搜索。

尽管这个特性并不常用,但它可以用来扩展一个包中的模块集合。

评论关闭