Python 3.6中如何处理“扁平化”相对导入?
解决Makefile调用脚本时的模块导入问题
我懂你已经仔细读过《Relative imports for the billionth time》那篇神文,但还是被项目里「通过Makefile运行脚本+跨子模块依赖」的导入问题绊住了对吧?结合你的需求——用make preprocessing/make train分别运行指定脚本,同时让所有模块始终从项目顶层加载,我给你两个最实用的解决方案:
方案1:通过Makefile设置PYTHONPATH(最直接)
先明确下常见的项目结构示例(如果你的结构略有不同,调整路径即可):
your_project/ ├── Makefile ├── src/ │ ├── __init__.py │ ├── data/ │ │ ├── __init__.py │ │ └── process.py │ └── models/ │ ├── __init__.py │ └── train_model.py └── utils/ ├── __init__.py └── common.py
在你的Makefile里,直接把项目顶层目录加入Python的搜索路径,然后调用脚本:
# 获取当前Makefile所在的项目顶层目录 PROJECT_ROOT := $(shell pwd) preprocessing: PYTHONPATH=$(PROJECT_ROOT) python src/data/process.py train: PYTHONPATH=$(PROJECT_ROOT) python src/models/train_model.py
为什么这招管用?
设置PYTHONPATH=$(PROJECT_ROOT)后,Python会把项目顶层当成模块搜索的根目录,你的所有脚本里都可以用绝对导入,比如在process.py里直接写:
from utils.common import load_raw_data from src.models.feature_utils import extract_features
完全不用纠结from ..xxx import ...这种相对导入的层级问题,跨子模块的依赖也能直接解决。
方案2:用python -m以模块方式运行(更符合Python规范)
如果不想手动设置PYTHONPATH,可以利用Python的模块运行机制,前提是所有子目录都要有__init__.py(空文件就行,用来标记这是一个Python模块)。
修改Makefile如下:
preprocessing: python -m src.data.process train: python -m src.models.train_model
注意事项
- 脚本里的执行代码一定要放在
if __name__ == "__main__":块里,不然当其他模块导入这个脚本时,会意外执行主逻辑。 - 这种方式同样会把项目顶层作为模块根目录,脚本里依然用绝对导入即可,跨子模块依赖轻松解决。
避坑提醒
千万别在脚本里用sys.path.append("/硬编码的路径")这种方式,换个环境或者项目目录移动后直接失效,用上面两种Makefile层面的方案才是稳定可靠的。
内容的提问来源于stack exchange,提问作者tmo
相关产品推荐
相关产品推荐

