Python项目内模块导入的Pythonic规范方法咨询
核心原则
Python导入系统区分脚本运行和包模块导入两种模式:
- 用
python -m <模块路径>运行时,当前工作目录会被加入sys.path,模块以包的一部分加载,支持相对导入。 - 直接运行
.py脚本时,脚本所在目录被加入sys.path,脚本作为__main__模块,不属于任何包,无法使用相对导入。
要避免导入问题,需统一运行方式,并遵循包结构规范。
一、规范项目结构与内部模块导入
针对你调整后的目录结构:
. ├── app │ ├── __init__.py │ ├── app.py │ └── inner │ ├── __init__.py │ └── utils.py # 假设这里有func_from_inner └── tests └── test_app.py
1. 应用内部模块导入
在app/app.py中导入inner模块时,始终使用相对导入:
# app/app.py from .inner.utils import func_from_inner
如果inner/__init__.py已经导出了func_from_inner,可以简化为:
from .inner import func_from_inner
2. 统一应用运行方式
不要直接运行python app/app.py,而是从项目根目录用模块方式运行:
python -m app.app
这样app会被当作包加载,相对导入有效,同时根目录在sys.path中,不会出现模块找不到的问题。
二、测试模块的导入问题解决
针对测试文件tests/test_app.py导入app模块的需求:
1. 给tests目录添加__init__.py
把tests也变成一个包,目录结构变为:
. ├── app │ ├── __init__.py │ ├── app.py │ └── inner │ └── __init__.py └── tests ├── __init__.py └── test_app.py
2. 测试文件中使用绝对导入
因为根目录在sys.path中(从根目录运行测试时),直接用绝对导入即可:
# tests/test_app.py from app.app import some_function from app.inner.utils import func_from_inner
3. 运行测试的正确方式
从项目根目录运行测试,推荐用python -m pytest(如果使用pytest),或者直接用模块方式运行测试文件:
# 用pytest(推荐) python -m pytest tests/test_app.py -v # 直接运行测试文件 python -m tests.test_app
这样测试文件作为tests包的一部分加载,同时根目录在sys.path中,能正常导入app模块。
三、为什么sys.path包含根目录还是无法递归导入?
直接运行python app/app.py时,虽然sys.path包含根目录,但from inner import ...仍然失败,原因是:
当直接运行app/app.py时,app.py的__name__是__main__,Python不会把它所在的app目录当作包,只会把app/目录加入sys.path。此时inner目录在app/下,但sys.path里的根目录下并没有直接的inner模块,所以from inner import ...找不到;而相对导入from .inner import ...又因为app.py不是包的一部分,所以报错。
只有用python -m app.app运行时,Python才会识别app是一个包,此时app.py作为包内的模块,相对导入才会生效,同时根目录在sys.path中,不会影响其他绝对导入。
总结:最佳实践
- 所有内部模块之间的导入,使用相对导入。
- 永远用
python -m <模块路径>的方式运行应用或测试,不要直接运行.py脚本。 - 把tests目录也变成包(添加
__init__.py),测试文件中用绝对导入导入app模块。 - 确保始终从项目根目录执行命令。
内容的提问来源于stack exchange,提问作者Łukasz

