
本文详解 Python 项目中因绝对导入误用导致的 ModuleNotFoundError,重点讲解如何在子包内正确使用相对导入(如 from .directory_handler import DirectoryNameHandler)来修复跨模块引用失败的问题。
本文详解 python 项目中因绝对导入误用导致的 `modulenotfounderror`,重点讲解如何在子包内正确使用相对导入(如 `from .directory_handler import directorynamehandler`)来修复跨模块引用失败的问题。
在 Python 包结构中,模块导入行为严格依赖于模块的执行上下文与导入语句的类型。你遇到的错误:
ModuleNotFoundError: No module named 'directory_handler'
根本原因在于:shelf.py 中使用了 绝对导入 from directory_handler import DirectoryNameHandler,但 directory_handler.py 并非顶层模块(即不在 sys.path 根目录下),而是位于 downloader_mod/ 子包内部。当 Python 解释器执行 downloader.py 时,仅将 src/ 目录加入模块搜索路径(sys.path),因此 directory_handler 无法被作为顶层模块解析。
而当你直接运行 shelf.py(例如 python shelf.py),Python 会将当前文件所在目录(即 src/downloader_mod/)设为工作目录,并将其加入 sys.path —— 此时 directory_handler 恰好成了“可导入的顶层模块”,所以能成功运行。但这属于偶然可行,违反了 Python 包的封装原则,且在正式导入链中必然失败。
✅ 正确解法:在包内模块间引用同级或子模块时,必须使用显式相对导入。
立即学习“Python免费学习笔记(深入)”;
修改 src/downloader_mod/shelf.py 的第 6 行:
# ❌ 错误:绝对导入,假设 directory_handler 是顶层模块 from directory_handler import DirectoryNameHandler # ✅ 正确:相对导入,表示“从当前包(.)中导入” from .directory_handler import DirectoryNameHandler
? 提示:. 表示当前包(即 downloader_mod),.. 表示上一级包,... 表示上两级,依此类推。此处 directory_handler.py 与 shelf.py 处于同一包层级,故用单点 .。
同时,请确保以下前提条件已满足(你已部分完成):
- src/downloader_mod/__init__.py 存在(即使为空),标识其为合法 Python 包;
- src/__init__.py 也建议存在(虽非强制,但推荐,便于未来扩展为嵌套包);
- 执行入口始终在 src/ 目录下(如 python downloader.py),或通过 -m 方式运行(如 python -m downloader_mod.shelf),以保证包结构被正确识别。
⚠️ 注意事项:
- 相对导入只能在包内模块中使用,不能在直接运行的脚本(如 python shelf.py)中使用,否则会报 SystemError: Parent module '' not loaded。因此,开发阶段应避免双击运行 .py 文件,而应统一通过 src/ 为根目录启动;
- VS Code 默认可能未正确设置 PYTHONPATH 或 cwd。可在 .vscode/settings.json 中显式指定工作目录:
{ "python.defaultInterpreterPath": "./venv/Scripts/python.exe", "python.testing.pytestArgs": ["src"], "terminal.integrated.env.windows": { "PYTHONPATH": "${workspaceFolder}/src" } }更推荐的方式是在 .vscode/launch.json 中配置调试入口:
{ "configurations": [ { "name": "Python: downloader.py", "type": "python", "request": "launch", "module": "downloader", "cwd": "${workspaceFolder}/src", "env": {"PYTHONPATH": "${workspaceFolder}/src"} } ] }
✅ 验证修复效果:
保存修改后,再次运行 downloader.py(确保终端位于 src/ 目录):
cd src python downloader.py
输出应正常显示类似:
sh.simple_dir = 'Generic_Feb25'
总结:Python 包内模块通信应遵循“显式优于隐式”原则。放弃模糊的绝对导入,改用清晰、可维护的相对导入,不仅能解决 ModuleNotFoundError,更能提升项目结构的健壮性与可移植性。记住口诀:同包用 .,跨包用 ..,入口不直跑,调试配 cwd。










