ModuleNotFoundError: No module named 'xxx'这句话看着像"没装这个库",实际一大半时候库是装好的,只不过装在了另一个解释器里,或者被当前目录下一个同名文件挡住了。排查顺序固定:先确定跑的是哪个Python,再看它去哪些目录找,最后才去翻代码。
先确认是哪个Python在跑
机器上装了多个版本是常态。直接敲pip install装上去的库,跟运行脚本用的未必是同一个解释器。三条命令就能看清:
python -c "import sys; print(sys.executable)"
pip -V
python -m pip install requests
pip -V输出的括号里那串路径,必须和sys.executable在同一个目录下,对不上就是装错了地方。所以装依赖一律用python -m pip install,别直接敲pip。Windows上还可以用py -0p列出所有已注册的Python,前面带星号的是默认那个。
之前遇到过一台服务器上python指向Python 3.9、3.10、3.11,三个版本并存,脚本是用systemd拉起来的,unit文件里写死了3.10的路径,而人登上去装包用的是默认的3.9,怎么装都报No module named。
看它到底去哪些目录找
python -m site输出的信息最全,sys.path、USER_SITE、ENABLE_USER_SITE三项都在里面:
python -m site
python -c "import sys, pprint; pprint.pprint(sys.path)"
sys.path的第一项决定了大部分莫名其妙的导入问题。直接跑python xxx.py时,第一项是脚本所在目录;用python -m pkg.xxx跑时,第一项是当前工作目录。Python 3.11、3.12,开始有了-P参数和PYTHONSAFEPATH,加上之后这两项都不再塞进sys.path,跑出来更干净,调试时很好用。
要临时加一个搜索目录,用PYTHONPATH环境变量;要长期加,在site-packages里丢一个.pth文件,一行一个路径,比改代码干净。
被同名文件挡住
这一类的报错很有迷惑性,报的是标准库里的东西出问题,比如json、email、queue、select、types、socket、ssl、random。原因往往是工程目录里有个json.py或者email/目录,因为脚本所在目录排在sys.path最前面,它把标准库顶掉了。
判断很简单,打印模块的真实路径:
python -c "import json; print(json.__file__)"
打印出来的如果不是lib/python3.x/json.py那种路径,而是工程目录里的某个文件,就是被挡了。改文件名,顺手把旁边的__pycache__删掉。见过最折腾的一次是报错信息里带着"partially initialized module",翻了半天才发现是目录里放了个types.py。
循环导入和相对导入
ImportError: cannot import name 'yyy' from 'zzz'有两种来源。一种是真的拼错了名字或者那个版本还没有这个接口,另一种是两个模块互相import,加载了一半拿不到对方的属性。Python 3.7、3.8,之后的报错信息会直接补一句most likely due to a circular import,看到这行就不用再怀疑名字了,去拆依赖:把公共的部分抽到第三个模块,或者把import挪到函数内部延迟执行。
attempted relative import with no known parent package也是常见的一条。包内模块写了from . import config,却用python service/main.py这种方式直接跑,模块被当成顶层脚本,没有父包,相对导入就失效。改成python -m service.main从包外面跑,或者在工程根目录建一个入口脚本。
目录结构与__init__.py
Python 3.3、3.4,之后有了PEP 420的隐式命名空间包,目录里不放__init__.py也能被导入,所以老教程里"必须有__init__.py"这句话现在不成立。但有差别:命名空间包和常规包同时存在时,带__init__.py的常规包优先;命名空间包也不支持在__init__里做初始化、不支持__all__。工程目录还是老老实实放一个空的__init__.py更省心。
另外目录名带连字符或者空格的,比如my-utils,没法直接import,因为连字符在Python里不是合法标识符,只能改目录名。
改名之后还在报旧错
文件删了、名字改了,导入还是指向老地方,基本就是__pycache__里的pyc在作怪,再就是sys.modules的缓存。清掉缓存目录再试:
find . -name __pycache__ -type d -exec rm -rf {} +
find . -name "*.pyc" -delete
进程内想刷新的话,importlib.invalidate_caches()可以强制重新扫目录,配合importlib.reload用。不过真碰上这种,直接重启进程更快。
打包成exe之后才报
本地跑得好好的,PyInstaller或者cx_Freeze打完包双击就报No module named。这类依赖是运行时动态import的,打包工具静态分析扫不到,得手动补。PyInstaller加--hidden-import=xxx,或者写进spec文件的hiddenimports列表;数据文件用--add-data带上。打包环境也要干净,在虚拟环境里打,别用装了一堆无关库的全局Python。
装了但就是找不到时,最后一步是确认这个库到底装在哪、带了哪些文件:
pip show -f requests
python -c "import requests; print(requests.__file__, requests.__version__)"
数据截至2026年10月,文中涉及的版本行为在Python 3.9、3.10、3.11、3.12、3.13、3.14,上均验证过;Python 3.14.0发布于2025年10月7日,Python 3.9系列最后一个版本是3.9.25,2025年10月31日停止支持。