如何让pip安装的Python包在两种运行方式下本地导入正常?
解决Python包在安装/未安装场景下的导入兼容问题
项目结构
$ tree . . ├── pyproject.toml └── src └── project ├── __init__.py ├── main.py └── module.py
现有文件内容
pyproject.toml
[project] name = "project" version = "0.1" [build-system] requires = ["setuptools>=61.0"] build-backend = "setuptools.build_meta" [project.scripts] launcher = "project.main:main" [tool.setuptools.packages.find] where = ["src"]
src/project/main.py
#!/usr/bin/env python import module def main(): module.function() if __name__ == "__main__": main()
注:module.py中的function()仅输出"Hello world!"
遇到的导入问题
- 直接使用
import module:通过pip install .安装后,调用launcher会触发ModuleNotFoundError,因为安装后Python会从site-packages查找模块,无法直接定位到同目录下的module - 使用
from project import module:安装后launcher可正常运行,但未安装时直接执行src/project/main.py会触发ModuleNotFoundError,此时Python找不到project包 - 使用相对导入
from . import module:仅在launcher调用时有效,直接运行脚本会触发ImportError(无已知父包) - 修改
sys.path的临时方案:虽然能兼容两种场景,但属于不优雅的hack,可能引入路径冲突
规范解决方案
方法1:绝对导入+脚本入口路径处理(推荐)
修改main.py,在直接运行脚本时自动将项目的src目录加入Python路径,同时保持绝对导入的规范:
#!/usr/bin/env python import sys from pathlib import Path # 仅在直接运行脚本时调整路径 if __name__ == "__main__": # 定位到项目根目录下的src文件夹 src_dir = Path(__file__).resolve().parent.parent.parent / "src" sys.path.append(str(src_dir)) # 使用标准绝对导入 from project import module def main(): module.function() if __name__ == "__main__": main()
这种方式的优势:
- 遵循Python包的绝对导入规范,避免相对导入的限制
- 仅在直接运行脚本时调整路径,不影响安装后的
launcher调用逻辑
方法2:使用-m参数以模块方式运行脚本
不需要修改任何代码,直接通过以下命令运行脚本:
python -m project.main
Python的-m参数会将当前工作目录(项目根目录,即包含src的目录)加入sys.path,此时Python会把project当作标准包处理,from project import module的绝对导入会正常生效,同时安装后的launcher也能正常工作。
这种方式完全符合Python的官方最佳实践,不需要修改代码或路径,是最推荐的本地调试方式。
关于打包配置的说明
你的pyproject.toml配置是完全正确的,问题核心并非打包配置,而是本地直接运行脚本时的Python路径问题。Python在运行脚本时默认只会把脚本所在目录加入sys.path,而以模块方式运行或安装包后,Python会从包安装路径查找模块,这两种场景的路径逻辑差异导致了导入问题。
内容的提问来源于stack exchange,提问作者Feasuro
相关产品推荐
相关产品推荐

