人人都会AI编程

6.3 包的组织结构:__init__.py 的作用、相对导入与绝对导入

更新时间:2026-07-12

随着项目代码量增长,把所有模块堆在一个文件夹里很快就会变得混乱。Python 通过(Package)来组织代码——包本质上就是一个包含 init.py 文件的目录,里面可以有子包和模块。理解 init.py 的作用,以及模块间的导入方式,是写出结构清晰、可维护项目的基本功。


init.py 的作用

init.py 是一个包目录下必须存在的文件(Python 3.3 以后允许省略,但强烈不推荐)。它主要有三个职责:

1. 标识目录为 Python 包
只要一个目录里放了 init.py,Python 就会把它识别成一个包。没有这个文件,目录只是一个普通文件夹,无法被 import 识别。

2. 初始化包时执行代码
当包被首次导入时,init.py 会自动执行一次。你可以在这里做:

  • 设置包级别的变量或常量。
  • 进行一次性初始化操作(比如检查依赖、读取配置)。
  • 预先导入子模块,简化外部使用(见后文)。

例如,一个数据分析包 analysis/init.py

# analysis/__init__.py
print("analysis 包已加载")

from . import cleaning    # 导入子模块
from . import statistics

当执行 import analysis 时,会输出提示,并把 cleaningstatistics 作为包的属性,可以直接 analysis.cleaning.xxx 使用。

3. 控制导出接口(all
init.py 中定义 all 列表,可以控制当用户使用 from package import * 时,哪些名字会被导入:

# analysis/__init__.py
__all__ = ['clean', 'analyze']

这能避免不必要的内部名字污染调用方的命名空间。


导入方式:绝对导入 vs 相对导入

包内部模块之间经常需要互相调用,这时就涉及到绝对导入相对导入的选择。

绝对导入
从项目根目录(通常是 sys.path 中包含的顶级包)开始,写出完整的导入路径。例如项目结构:

project/
├── main.py
└── mypkg/
    ├── __init__.py
    ├── module_a.py
    └── sub/
        ├── __init__.py
        └── module_b.py

module_b.py 中想用 module_a 里的函数,可以用绝对导入:

# mypkg/sub/module_b.py
from mypkg import module_a

优点:路径一目了然,不受当前模块位置影响;在模块移动后容易修改变更点;IDE 支持通常更好。
推荐:绝大多数情况下优先使用绝对导入。

相对导入
使用 . 表示当前包,.. 表示上级包,适用于包内部模块间的调用。例如同样的 module_b.py 可以这样写:

# mypkg/sub/module_b.py
from .. import module_a        # 回到 mypkg 包,导入 module_a
from . import module_b_extra   # 从当前 sub 包导入同级的 module_b_extra

限制:相对导入只能用在包内部,不能在顶层脚本(main)中使用,否则会抛出 ImportError
场合:当包结构深层且不想写出长长的绝对路径时,或者希望在重命名顶层包时内部代码不受影响,可以使用相对导入。但注意不要滥用到让代码难以理解。

两者对比
| 维度 | 绝对导入 | 相对导入 |
|------|----------|----------|
| 可读性 | 清晰,直接看出模块位置 | 需要心算层级,嵌套深时易混淆 |
| 重命名包名 | 需要全局修改路径 | 内部不受影响(只要相对层级不变) |
| 灵活性 | 模块移动到不同层级需改路径 | 移动模块时相对关系可能变化 |
| 适用场景 | 包外部导入、顶层脚本 | 包内部、结构稳定的深层调用 |


最佳实践总结

  • 始终保留 init.py,即使内容是空的。这能避免许多隐晦的导入错误,并明确告诉协作者“这是一个包”。
  • init.py 中有选择地导入常用子模块,可以让外部用户更容易使用,比如 import mypkg 后直接 mypkg.do_something(),而不需要层层写 mypkg.sub.module.do_something()
  • 优先使用绝对导入,路径清晰,维护友好。只在包内部且希望保持相对独立性时,才谨慎使用相对导入。
  • 避免写 from module import *,容易污染命名空间并导致不可追踪的错误。如果要让用户快速访问,在 init.py 里显式控制。