本地pip安装包的相对导入问题:ModuleNotFoundError与ImportError
项目结构
- DIR_PROJECT
- pyproject.toml
- main.py
- DIR_MODULES
- module.py
代码内容
module.py
def f1(): print("Module ok")
初始__main__.py
#!/usr/bin/env python3 from DIR_MODULES import f1 def main(): f1() if __name__ == '__main__': main()
遇到的问题
直接运行__main__.py时一切正常,但通过python -m pip install -e /path/to/DIR_PROJECT本地安装后执行程序,会抛出ModuleNotFoundError: No module named 'DIR_MODULES'。
如果把导入语句改成相对导入from .DIR_MODULES import f1,安装后的程序能正常运行,但直接运行__main__.py时又会报错ImportError: attempted relative import with no known parent package。
不想维护两份导入代码(开发用绝对导入、发布用相对导入),试过加__init__.py无效,也换了不同目录运行脚本都没解决,目前临时方案是在__main__.py中加路径处理代码:
from pathlib import Path import sys if __package__ is None: DIR = Path(__file__).resolve().parent sys.path.insert(0, str(DIR.parent)) __package__ = DIR.name
可行解决方案
方案1:统一绝对导入+配置pyproject.toml
在pyproject.toml里明确项目名称,比如设为myproject:
[project] name = "myproject" version = "0.1.0"
然后把导入语句改成from myproject.DIR_MODULES import f1。这样:
- 安装后运行:因为包已被pip识别,绝对导入能正常找到模块
- 开发时:从
DIR_PROJECT的父目录执行python -m myproject,就能以模块方式运行脚本,导入正常
方案2:完善路径处理逻辑(兼容两种运行方式)
优化临时方案,让代码在直接运行和安装后都能正常工作,完整__main__.py代码如下:
#!/usr/bin/env python3 import sys from pathlib import Path # 处理直接运行脚本时的包路径问题 if __package__ is None: current_file = Path(__file__).resolve() project_root = current_file.parent # 将项目根目录加入Python路径 sys.path.insert(0, str(project_root)) # 手动设置包名,让导入生效 __package__ = project_root.name from DIR_MODULES import f1 def main(): f1() if __name__ == '__main__': main()
这个逻辑会自动判断运行场景,调整路径和包名,让导入语句在两种模式下都能识别到DIR_MODULES。
方案3:开发时始终以模块方式运行
放弃直接运行__main__.py,开发时从DIR_PROJECT的父目录执行命令:
python -m DIR_PROJECT
此时脚本会作为模块运行,__package__会被正确设置,用相对导入from .DIR_MODULES import f1就能同时支持开发和安装后的场景,无需额外代码处理。
内容的提问来源于stack exchange,提问作者mkp

