Python import 找不到模块的根本原因是 sys.path 未包含目标模块路径,其值由 PYTHONPATH、安装路径、当前目录等动态构建,且不同启动方式(如 python script.py 与 python -m package.module)对 sys.path[0] 和包结构要求不同。

Python 的 import 为什么有时找不到模块
根本原因不是代码写错了,而是 Python 没在它“愿意找”的地方发现目标模块。这个“愿意找”的范围由 sys.path 决定,而它本身是动态构建的:启动时从环境变量 PYTHONPATH、安装路径、当前目录(仅对顶层脚本有效)等拼接而来。如果你用 python myscript.py 运行,当前目录会加入 sys.path[0];但用 python -m mypackage.mymodule 启动时,sys.path[0] 是空字符串(即当前工作目录),且包结构必须合规。
常见错误现象:
- 在子目录里执行
python main.py,里面import utils报错 —— 因为utils.py所在目录没进sys.path - 用
python -m运行却提示No module named 'xxx'—— 很可能当前目录不是包根,或缺少__init__.py(Python 3.3+ 的隐式命名空间包除外)
__init__.py 到底要不要写、写什么
它不是摆设,而是 Python 识别「这是一个包」的显式信号(除 PEP 420 命名空间包外)。空文件即可满足基本要求,但它的内容直接影响导入行为:
- 留空:
import mypackage成功,但不会自动导入子模块 - 写
from . import submod:import mypackage会顺带加载submod,方便用户简化调用 - 写
__all__ = ['submod']:配合from mypackage import *控制暴露接口(不推荐滥用) - 含执行逻辑(如日志、配置初始化):每次
import mypackage都会运行 —— 注意副作用
注意:Python 3.3+ 支持无 __init__.py 的命名空间包,但仅适用于分散在不同路径下的同名包合并场景,日常项目别依赖它来“省事”。
立即学习“Python免费学习笔记(深入)”;
相对导入(from . import xxx)为何总报 SystemError 或 ImportError
相对导入只在包内有效,且必须通过 -m 方式启动,不能直接运行 .py 文件。这是因为相对导入需要 Python 知道模块的「绝对位置」,而直接执行文件时,它的 __name__ 是 '__main__',没有包层级信息。
- ✅ 正确:项目结构
proj/ pkg/ __init__.py mod1.py mod2.py,在proj/目录下运行python -m pkg.mod1,其中mod1.py写from . import mod2 - ❌ 错误:直接
python pkg/mod1.py,哪怕路径正确,也会报Attempted relative import with no known parent package - ⚠️ 坑点:
from .. import parent要求上级目录也有合法包结构(含__init__.py),否则层级计算失败
如何安全地修改 sys.path 让导入更可控
临时加路径最常用也最危险 —— 容易污染全局、引发版本冲突或掩盖真实问题。优先级顺序是:手动插入 > PYTHONPATH > 默认路径。建议按以下顺序判断是否真需修改:
- 能用
python -m启动,就别改sys.path - 测试时想快速接入本地模块?用
-m pip install -e .做可编辑安装,一劳永逸 - 确实要动态加路径(如插件系统),用
sys.path.insert(0, '/abs/path/to/dir'),但务必检查路径存在且是绝对路径(os.path.abspath()处理) - 避免在模块顶层反复
append,容易重复添加;更别放在__init__.py里——会随每次导入执行
最常被忽略的一点:修改 sys.path 不影响已缓存的模块(sys.modules)。如果之前导入失败过,缓存里可能留着 None,后续即使路径加对了,仍会报错 —— 此时需手动清理 sys.modules 中对应键,或重启解释器。










