找回密码
 立即注册
搜索
到手67.99元!阿里云轻量2核2G云服务器
查看: 31|回复: 0

[复制链接]

696

主题

0

回帖

11

积分

管理员

积分
11
发表于 2026-4-21 09:18:05 | 显示全部楼层 |阅读模式
包是通过使用“带点号模块名”来构造 Python 模块命名空间的一种方式。 例如,模块名 A.B 表示名为 A 的包中名为 B 的子模块。 就像使用模块可以让不同模块的作者不必担心彼此的全局变量名一样,使用带点号模块名也可以让 NumPy 或 Pillow 等多模块包的作者也不必担心彼此的模块名冲突。

假设要为统一处理声音文件与声音数据设计一个模块集(“包”)。声音文件的格式很多(通常以扩展名来识别,例如:.wav,.aiff,.au),因此,为了不同文件格式之间的转换,需要创建和维护一个不断增长的模块集合。为了实现对声音数据的不同处理(例如,混声、添加回声、均衡器功能、创造人工立体声效果),还要编写无穷无尽的模块流。下面这个分级文件树展示了这个包的架构:

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/                  用于过滤器的子包
              __init__.py
              equalizer.py
              vocoder.py
              karaoke.py
              ...
导入包时,Python 搜索 sys.path 里的目录,查找包的子目录。

需要有 __init__.py 文件才能让 Python 将包含该文件的目录当作包来处理(除非使用 namespace package,这是一个相对高级的特性)。 这可以防止重名的目录如 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)
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 都必须是包;最后一项可以是模块或包,但不能是上一项中定义的类、函数或变量。

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 包的三个命名子模块。

请注意子模块可能会受到本地定义名称的影响。 例如,如果你在 sound/effects/__init__.py 文件中添加了一个 reverse 函数,from sound.effects import * 将只导入 echo 和 surround 这两个子模块,但 不会 导入 reverse 子模块,因为它被本地定义的 reverse 函数所遮挡:

__all__ = [
    "echo",      # 指向 'echo.py' 文件
    "surround",  # 指向 'surround.py' 文件
    "reverse",   # !!! 现在指向 'reverse' 函数 !!!
]

def reverse(msg: str):  # <-- 此名称将覆盖 'reverse.py' 子模块
    return msg[::-1]    #     针对 'from sound.effects import *' 的情况
如果没有定义 __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 *
在本例中,echo 和 surround 模块被导入到当前命名空间,因为在执行 from...import 语句时它们已在 sound.effects 包中定义了。 (当定义了 __all__ 时也是如此)。

虽然,可以把模块设计为用 import * 时只导出遵循指定模式的名称,但仍不提倡在生产代码中使用这种做法。

记住,使用 from package import specific_submodule 没有任何问题! 实际上,除了导入模块使用不同包的同名子模块之外,这种方式是推荐用法。

2. 相对导入
当包由多个子包构成(如示例中的 sound 包)时,可以使用绝对导入来引用同级包的子模块。 例如,如果 sound.filters.vocoder 模块需要使用 sound.effects 包中的 echo 模块,它可以使用 from sound.effects import echo。

你还可以编写相对导入代码,即使用 from module import name 形式的 import 语句。 这些导入使用前导点号来表示相对导入所涉及的当前包和上级包。 例如对于 surround 模块,可以使用:

from . import echo
from .. import formats
from ..filters import equalizer
需要注意的是,相对导入是基于当前模块所属包的名称进行的。由于主模块(即直接运行的脚本)没有所属包,因此那些打算作为 Python 应用程序主模块使用的模块,必须始终使用绝对导入。

3. 多目录中的包
包还支持一个特殊的属性, __path__ 。 在执行该文件中的代码之前,它被初始化为字符串的 sequence,其中包含包的 __init__.py 的目录名称。这个变量可以修改;修改后会影响今后对模块和包中包含的子包的搜索。

这个功能虽然不常用,但可用于扩展包中的模块集。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|skypiea

GMT+8, 2026-6-27 03:58 , Processed in 0.043782 second(s), 20 queries .

Powered by skypiea

快速回复 返回顶部 返回列表