File Structure Example
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
...How to use
Example
# Use Method 1
import sound.effects.echo
# Use Method 2
from sound.effects import echo
# Use Method 3
from sound.effects.echo import echofilterNote
注意,使用
from package import item时,item 可以是包的子模块(或子包),也可以是包中定义的函数、类或变量等其他名称。import语句首先测试包中是否定义了 item;如果未在包中定义,则假定 item 是模块,并尝试加载。如果找不到 item,则触发 [ImportError]异常。
Notice
import *
使用 from sound.effects import * 时会发生什么?你可能希望它会查找并导入包的所有子模块,但事实并非如此。因为这将花费很长的时间,并且可能会产生你不想要的副作用。
解决办法是提供包的显式索引
import语句使用如下惯例:如果包的 __init__.py 代码定义了列表 __all__,运行 from package import * 时,它就是被导入的模块名列表。发布包的新版本时,包的作者应更新此列表。如果包的作者认为没有必要在包中执行导入 * 操作,也可以不提供此列表。
Example:
__all__ = [
"echo", # refers to the 'echo.py' file
"surround", # refers to the 'surround.py' file
"reverse", # !!! refers to the 'reverse' function now !!!
]
def reverse(msg: str): # <-- this name shadows the 'reverse.py' submodule
return msg[::-1] # in the case of a 'from sound.effects import *'同时,有语法可以shadow submodule,如上述所示;因为本地定义的reverse函数会遮挡子模块。
Note
如果没有定义
__all__,from sound.effects import *语句 不会 把包sound.effects中的所有子模块都导入到当前命名空间;它只是确保包sound.effects已被导入(可能还会运行__init__.py中的任何初始化代码),然后再导入包中定义的任何名称。 这包括由__init__.py定义的任何名称(以及显式加载的子模块)。 它还包括先前import语句显式加载的包里的任何子模块。 请看以下代码:
相对导入
from . import echo
from .. import formats
from ..filters import equalizer这些导入使用前导点号来表示相对导入所涉及的当前包和上级包
Abstract
相对导入基于当前模块名。因为主模块名永远是
"__main__",所以如果计划将一个模块用作 Python 应用程序的主模块,那么该模块内的导入语句必须始终使用绝对导入。
Template Example
Complex one
package_name/
│
├── package_name/
│ ├── __init__.py
│ └── module_name.py
│
├── tests/
│ ├── __init__.py
│ └── test_module_name.py
│
├── .gitignore
├── LICENSE
├── README.md
├── requirements.txt
├── setup.py
└── pyproject.tomlpackage_name/:包的顶层目录。package_name/__init__.py:空文件告诉Python这个目录应该被视为一个包。package_name/module_name.py:代码所在。tests/:测试代码。.gitignore:这个文件告诉git哪些文件不应该被版本控制系统跟踪。LICENSE:包的许可证文件。README.md:描述包的功能和使用方法。requirements.txt:列出包的依赖项。setup.py:这是一个构建和安装包的脚本。pyproject.toml:这是一个新的Python包配置文件,用于存储包的元数据和构建信息。
Easy one
my_package/
│
├── __init__.py
└── module.pyHow to code __init__
- 空的
__init__.py文件:
# 这个 __init__.py 文件是空的- 在
__init__.py文件中导入包内模块,以便外部直接导入:
from .module1 import *
from .module2 import some_function- 在
__init__.py文件中设置__all__变量,控制使用from package import *时哪些符号会被导入:
__all__ = ['module1', 'module2', 'some_function']- 在
__init__.py文件中执行包的初始化代码,如配置日志等:
import logging
logging.basicConfig(level=logging.INFO)Notice
Hint
在Python包的结构中,
__init__.py文件起着一个重要的作用,它定义了包的属性和方法。当您从一个包中导入模块时,__init__.py文件会被自动执行。因此,您可以在这个文件中指定需要导出的模块,这样外部就可以直接导入这些模块,而不需要知道它们具体在包的哪个位置。
from .module1 import *
from .module2 import some_function这里的 . 表示当前包目录。from .module1 import * 表示从当前包目录下的 module1.py 文件中导入所有函数和类。from .module2 import some_function 表示仅从 module2.py 文件中导入 some_function 函数。
这样做的好处是,当其他人使用这个包时,他们可以更方便地访问这些函数和类,而不需要知道具体的模块路径。例如,他们可以这样导入:
from my_package import some_function而不是:
from my_package.module2 import some_function这样可以使得包的结构对用户更加透明,用户只需要关注他们需要的功能,而不是包的内部结构。这也有助于在不影响用户的情况下重构包的内部结构。当然,这种方式也需要注意不要导出太多不必要的内部细节,以免造成命名空间的污染。