
本文详解 Python 多目录结构中跨包导入的规范方法,重点解决 ModuleNotFoundError: No module named 't1' 等常见导入错误,涵盖相对导入语法、__init__.py 的作用与现代 Python 兼容性说明,并提供可立即验证的项目结构与代码示例。
本文详解 python 多目录结构中跨包导入的规范方法,重点解决 `modulenotfounderror: no module named 't1'` 等常见导入错误,涵盖相对导入语法、`__init__.py` 的作用与现代 python 兼容性说明,并提供可立即验证的项目结构与代码示例。
在 Python 项目中,当模块分散在多层子目录(如 t1/test.py 和 t2/main.py)时,直接使用 import t1.test 或 from t1 import test 往往会触发 ModuleNotFoundError —— 这并非代码写错,而是 Python 解释器未将当前目录识别为可导入的包路径。根本原因在于:Python 只会在 sys.path 中的路径下搜索模块,而默认不包含当前工作目录的父级或同级子包。
✅ 正确方案:使用相对导入(Relative Import)
若 main.py 位于 t2/ 目录下,需导入同级目录 t1/ 中的 test.py,应采用 包内相对导入,语法为:
from ..t1 import test
? 说明:
..表示向上回退两级(t2→ 项目根目录),t1是根目录下的子包名,test是该包内的模块。此写法仅在main.py作为包内模块被运行(即通过-m方式执行)时有效。
? 项目结构与必要配置
推荐的标准结构如下(以 TESTCASE 为项目根目录):
TESTCASE/
├── __init__.py # 可选(Python 3.3+ PEP 420 隐式命名空间包支持无 init)
├── t1/
│ ├── __init__.py # 建议保留,明确标识为包(兼容性 & 可读性更佳)
│ └── test.py
└── t2/
├── __init__.py
└── main.py
-
t1/test.py示例内容:def hello(): return "Hello from t1.test!" -
t2/main.py正确导入与调用:from ..t1 import test # ✅ 相对导入(必须在包上下文中执行) if __name__ == "__main__": print(test.hello()) # 输出:Hello from t1.test!
⚠️ 关键注意事项
❌ 禁止直接运行
python t2/main.py:这会使main.py以__main__模块身份启动,Python 不将其视为包的一部分,相对导入会报ImportError: attempted relative import with no known parent package。-
✅ 正确执行方式:在
TESTCASE/根目录下,使用-m参数运行:python -m t2.main
此时 Python 将
TESTCASE/视为顶层包,t2.main成为子模块,相对导入生效。 __init__.py的现代实践:Python 3.3+ 支持隐式命名空间包(PEP 420),理论上可省略__init__.py。但强烈建议保留空的__init__.py—— 它显式声明包边界,避免 IDE 误判、提升可维护性,并确保向后兼容。
? 替代方案(不推荐用于包内组织)
若因历史原因无法使用 -m 执行,可临时修改 sys.path(仅限调试):
import sys from pathlib import Path sys.path.insert(0, str(Path(__file__).parent.parent)) # 将根目录加入路径 from t1 import test # ✅ 现在可直接绝对导入
但该方式破坏封装性,不应出现在生产代码中。
✅ 总结
解决跨目录模块导入的核心是理解 Python 的包执行模型:
① 用 __init__.py 明确包结构;
② 在包内使用 from ..package import module 进行相对导入;
③ 始终通过 python -m package.module 启动,而非直接执行 .py 文件。
遵循这三点,即可稳健处理任意深度的嵌套包导入问题。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











