包是一种用“点式模块名”构造 Python 模块命名空间的方法。例如,模块名 A.B
表示包 A
中名为 B
的子模块。正如模块可以区分不同模块之间的全局变量名称一样,点式模块名可以区分 NumPy 或 Pillow 等不同多模块包之间的模块名称。
假设要为统一处理声音文件与声音数据设计一个模块集(“包”)。声音文件的格式很多(通常以扩展名来识别,例如:.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
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
里的目录,查找包的子目录。
The __init__.py
files are required to make Python treat directories containing the file as packages. This prevents directories with a common name, such as string
, from unintentionally hiding valid modules that occur later on the module search path. In the simplest case, __init__.py
can just be an empty file, but it can also execute initialization code for the package or set the __all__
variable, described later.
还可以从包中导入单个模块,例如:
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)
Import 语句的另一种变体是直接导入所需的函数或变量:
from sound.effects.echo import echofilter
同样,这样也会加载子模块 echo
,但可以直接使用函数 echofilter()
:
echofilter(input, output, delay=0.7, atten=4)
注意,使用 from package import item
时,item 可以是包的子模块(或子包),也可以是包中定义的函数、类或变量等其他名称。import
语句首先测试包中是否定义了 item;如果未在包中定义,则假定 item 是模块,并尝试加载。如果找不到 item,则触发 ImportError 异常。
相反,使用 import item.subitem.subsubitem
句法时,除最后一项外,每个 item 都必须是包;最后一项可以是模块或包,但不能是上一项中定义的类、函数或变量。
6.4.1. 从包中导入 *
使用 from sound.effects import *
时会发生什么?理想情况下,该语句在文件系统查找并导入包的所有子模块。这项操作花费的时间较长,并且导入子模块可能会产生不必要的副作用,这种副作用只有在显式导入子模块时才会发生。
唯一的解决方案是提供包的显式索引。import 语句使用如下惯例:如果包的 __init__.py
代码定义了列表 __all__
,运行 from package import *
时,它就是用于导入的模块名列表。发布包的新版本时,包的作者应更新此列表。如果包的作者认为没有必要在包中执行导入 * 操作,也可以不提供此列表。例如,sound/effects/__init__.py
文件包含以下代码:
__all__ = ["echo", "surround", "reverse"]
这将意味着将 from sound.effects import *
导入 sound.effects
包的三个命名的子模块。
如果没有定义 __all__
,from sound.effects import *
语句 不会 把包 sound.effects
中所有子模块都导入到当前命名空间;该语句只确保导入包 sound.effects
(可能还会运行 __init__.py
中的初始化代码),然后,再导入包中定义的名称。这些名称包括 __init__.py
中定义的任何名称(以及显式加载的子模块),还包括之前 import 语句显式加载的包里的子模块。请看以下代码:
import sound.effects.echo
import sound.effects.surround
from sound.effects import *
本例中,执行 from...import
语句时,将把 echo
和 surround
模块导入至当前命名空间,因为,它们是在 sound.effects
包里定义的。(该导入操作在定义了 __all__
时也有效。)
虽然,可以把模块设计为用 import *
时只导出遵循指定模式的名称,但仍不提倡在生产代码中使用这种做法。
记住,使用 from package import specific_submodule
没有任何问题! 实际上,除了导入模块使用不同包的同名子模块之外,这种方式是推荐用法。