1.7.3 包

一个包(package)就是放在一个文件夹里的模块集合。包的名字就是文件夹的名字。我们需要做的是告诉Python这个文件夹是一个包,并且把一个名为__init__.py的文件(通常是空的)放在这个文件夹里,如图1-40所示。包是一种管理Python模块命名空间的形式,采用“点模块名称”。比如一个模块的名称是package_a.module_a1,那么它表示一个包package_a中的子模块module_a1。

图1-40 包与模块的结构图

【例1-72】一种可能的包结构(在分层的文件系统中)


sound                     # 顶层包
    __init__.py           # 初始化sound包
formats                   # 文件格式转换子包
    __init__.py
    wavread.py
    wavwrite.py
    aiffread.py    
    aiffwrite.py
    auread.py
    auwrite.py
    ...
effects                   # 声音效果子包
    __init__.py   
    echo.py
    surround.py
    reverse.py
    ...
filters                   # filters子包
    __init__.py
    equalizer.py
    vocoder.py
    karaoke.py
    ...

1.包的导入

在导入一个包的时候,Python会根据sys.path中的目录来寻找这个包中包含的子目录。目录只有包含一个名为__init__.py的文件才会被认作一个包,主要是为了避免一些常见的名字(比如string)不小心影响了搜索路径中的有效模块。最简单的情况,放一个空的:file:__init__.py就可以了。当然,这个文件中也可以包含一些初始化代码,或者为(将在后面介绍的)__all__变量赋值。

【例1-73】导入一个包中的特定模块


import sound.effects.echo

这将会导入子模块:sound.effects.echo,其必须使用全名去访问。

【例1-74】使用全名访问


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

【例1-75】导入子模块


from sound.effects import echo

这同样会导入子模块:echo,并且它不需要那些冗长的前缀。

【例1-76】导入子模块:echo


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

【例1-77】直接导入一个函数或者变量


from sound.effects.echo import echofilter

同样,这种方法会导入子模块:echo,并且可以直接使用它的echofilter()函数。

【例1-78】导入子模块:echo并使用echofilter()函数


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

注意 当使用from package import item这种形式的时候,对应的item既可以是包里面的子模块(子包),也可以是包里面定义的其他名称,比如函数、类或者变量。

import语法会首先把item当作一个包定义的名称,如果没找到,再试图按照一个模块去导入,如果还没找到,那么便会抛出一个:exc:ImportError异常。反之,如果使用形如import item.subitem.subsubitem的导入形式,那么除了最后一项,其余项都必须是包,而最后一项则可以是模块或者包,但不可以是类、函数或者变量的名字。

2.导入包中所有子模块

Python会进入文件系统,找到这个包里面所有的子模块,并把它们都导入进来。

注意 导入语句遵循如下规则:如果包定义文件__init__.py存在一个名为__all__的列表变量,那么在使用from package import *时就会把这个列表中的所有名字作为包内容导入。

【例1-79】file:sounds/effects/__init__.py中包含的代码


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

这表示当使用from sound.effects import *这种语法时,只会导入包中的这三个子模块。如果__all__真的没有定义,那么使用from sound.effects import *这种语法的时候,就不会导入包sound.effects里的任何子模块。它只是把包sound.effects和它里面定义的所有内容导入进来(可能运行__init__.py里定义的初始化代码)。这会把__init__.py里面定义的所有名字都导入进来,并且它不会破坏我们在这句话之前导入的所有明确指定的模块。

【例1-80】包的导入


import sound.effects.echo
import sound.effects.surround
from sound.effects import *

这个例子中,在执行from...import前,包sound.effects中的echo和surround模块都被导入当前的命名空间中了。通常我们并不主张使用*这种方法来导入模块,因为这种方法经常会导致代码的可读性降低,不过这样导入的确可以省去不少敲代码的功夫。

注意 使用from Package import specific_submodule这种方法永远不会有错。事实上,这也是一般推荐使用的方法,除非你要导入的子模块有可能和其他包的子模块重名。如果在结构中包是一个子包(比如在这个例子中,对于包sound来说),而你又想导入兄弟包(同级别的包),那么你就需要使用绝对的路径来实现导入。比如,如果模块sound.filters.vocoder要使用包sound.effects中的模块echo,那么你就要写成from sound.effects import echo。

【例1-81】from...import应用


from . import echo
from .. import formats
from .. filters import equalizer

无论是隐式的还是显式的相对导入,都是从当前模块开始的。主模块的名字永远是__main__,一个Python应用程序的主模块应当总是使用绝对路径引用。包还提供一个额外的属性__path__。这是一个目录列表,里面每一个包含的目录都有为这个包服务的__init__.py,需要在其他__init__.py被执行前定义。可以修改这个变量,以影响包含在包里面的模块和子包。这个功能并不常用,一般可用来扩展包中的模块。