根本区别是:python xxx.py 将脚本所在目录设为 sys.path[0],python -m package.module 将当前工作目录(cwd)设为 sys.path[0](空字符串 ''),这决定模块能否被成功导入。

python -m 和 python xxx.py 的 sys.path 差异在哪
根本区别就一句话:python xxx.py 把脚本所在目录加进 sys.path[0];python -m package.module 把当前工作目录(CWD)加进 sys.path[0]。这直接决定模块能不能被 import 成功。
比如你项目结构是:
myproject/
├── src/
│ ├── __init__.py
│ └── core.py
└── tests/
└── test_core.py
你在 myproject/ 目录下执行:
-
python -m pytest tests/test_core.py→ CWD 是myproject/,所以src/可被识别为包 -
python tests/test_core.py→sys.path[0]变成myproject/tests/,src/不在路径里,ImportError
为什么相对导入(from . import xxx)必须用 python -m
相对导入依赖 __package__ 属性,而这个属性只在模块模式下才有值。直接运行 python xxx.py 时,脚本的 __name__ 是 '__main__',__package__ 是 None,Python 根本不知道 “.” 指向哪一级包。
常见报错:
ValueError: attempted relative import beyond top-level packageSystemError: Parent module '' not loaded, cannot perform relative import
只要看到这类错误,基本可以确定你用了 python xxx.py 启动一个含 from . 或 from .. 的文件——换成 python -m package.submodule 就好,且确保不带 .py 后缀。
pytest 找不到本地模块时该用哪个命令
当你运行 pytest test_xxx.py 报 ModuleNotFoundError: No module named 'src',但 python -m pytest test_xxx.py 正常,说明 pytest 可执行文件没继承 CWD 到 sys.path。
有三个稳妥解法:
- 统一用
python -m pytest [args],最省心,行为与 Python 模块加载逻辑一致 - 把项目安装为可编辑包:
pip install -e .(需有pyproject.toml或setup.py) - 手动加路径:在测试文件开头插入
sys.path.insert(0, str(Path(__file__).parent.parent)),但污染测试逻辑,不推荐
注意:pytest 命令本身不是 Python 内置命令,它本质是调用 venv/Scripts/pytest.exe(Windows)或 venv/bin/pytest(Unix),不走 python -m 的路径注入机制。
怎么快速验证当前 sys.path 是什么
写个临时脚本 debug_path.py,内容就一行:
import sys; print('\n'.join(sys.path))
然后分别运行:
-
python debug_path.py→ 看第一行是不是脚本所在目录 -
python -m debug_path→ 看第一行是不是空字符串''(代表 CWD)
这个空字符串 '' 很关键:它让 Python 在当前目录下查找包,而不是在脚本目录下找。很多新手卡在这里,以为“脚本能跑通就行”,结果一换目录或加 CI 就崩。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











