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 echofilter

Note

注意,使用 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.toml
  • package_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.py

How to code __init__

  1. 空的 __init__.py 文件:
# 这个 __init__.py 文件是空的
  1. 在 __init__.py 文件中导入包内模块,以便外部直接导入:
from .module1 import *
from .module2 import some_function
  1. 在 __init__.py 文件中设置 __all__ 变量,控制使用 from package import * 时哪些符号会被导入:
__all__ = ['module1', 'module2', 'some_function']
  1. 在 __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

这样可以使得包的结构对用户更加透明,用户只需要关注他们需要的功能,而不是包的内部结构。这也有助于在不影响用户的情况下重构包的内部结构。当然,这种方式也需要注意不要导出太多不必要的内部细节,以免造成命名空间的污染

Reference