昨天下午我手贱,模块在服务器上跑了个`pip install --upgrade`,缺失请手结果升级到一半,动删网络断了。除模重启终端后,块残Python 环境直接瘫痪——import 什么模块都报错,余文连`requests`都进不来。模块最后折腾了半小时,缺失请手发现是动删`setuptools`的残留文件把整个site-packages目录给污染了。
这事儿不罕见。除模Python 的块残模块卸载机制从来就不是“干净”的。`pip uninstall` 只会删除它记录在案的余文文件,可有些包在安装时偷偷往外写东西——比如`__pycache__`、模块`.pyc`文件、缺失请手或者直接扔一个配置文件到`/etc`或者用户目录下。动删你卸载了主包,这些散兵游勇还在系统里晃荡,轻则占空间,重则让后续安装的新版本模块直接崩溃。
我见过最典型的案例是`numpy`。旧版本卸载不干净,新版本装上去后,import 时报错“numpy.core.multiarray failed to import”。原因就是老的`.so`文件残留在`site-packages/numpy/core/`里,跟新版的二进制文件冲突。你光看报错以为是代码问题,查半天才发现是残留文件在作妖。
怎么判断有没有残留?最简单的方法:卸载模块后,去`site-packages`目录里搜包名。比如你卸载了`flask`,但目录下还有`flask`文件夹或者`Flask-*.dist-info`目录,那就说明没删干净。另外,Python 3.8 以上的版本会在`__pycache__`里生成缓存,这些文件不会随卸载自动清除,得手动删。
删的时候别急着`rm -rf`。先确认路径,用`python -c "import site; print(site.getsitepackages())"`看看实际路径。有些环境用虚拟环境,路径会不同。删完再用`importlib.util.find_spec('模块名')`验证一下——如果返回`None`,说明清干净了。
还有个隐蔽的坑:`dist-info`目录。这玩意儿是 pip 的元数据,卸载时通常会被移除,但如果你曾经手动装过某个包,或者用了`--target`参数,它可能会残留在别的位置。搜索时别只看`site-packages`,全局搜一下`*.dist-info`,把对应模块的删掉。
最后说个土办法。你要是实在找不到残留文件,直接重装一遍相同版本的模块,再卸载。这招对绝大多数情况有效——重装会覆盖所有旧文件,卸载时就能干净地全部移除。我自己的习惯是,每次升级大版本模块前,先强制卸载,然后手动清一遍`site-packages`里的孤儿文件,再装新的。麻烦是麻烦点,但好过在深夜被诡异的 import 报错搞得头皮发麻。
记住,Python 不是 C++,它没有“卸载器”这种概念。模块就是一堆文件,删不干净就得自己动手。别嫌麻烦,这是每个 Python 开发者迟早要面对的脏活。