Python 3.12 正式移除了 distutils、binhex、aifc、sunau、audioop 和 imp 六个长期弃用模块,其中 distutils 影响最广,直接导致依赖它的构建脚本报 ModuleNotFoundError。

Python 3.12 并没有主动“删除”标准库,而是正式移除了长期标记为 Deprecated、早已无人维护、或已被更优替代方案覆盖的模块——这些模块就是所谓“死电池”(dead batteries)。它们不是突然消失,而是多年弃用后自然退役。
哪些模块在 3.12 中被真正移除了?
根据官方 What's New in Python 3.12 和 sys.stdlib_module_names 对比结果,以下模块已从标准库中彻底剔除:
-
distutils:自 3.12 起完全移除,不再提供任何子模块(如distutils.core、distutils.util) -
binhex:Python 2 遗留编码工具,3.12 正式删除(3.11 已弃用) -
aifc、sunau、audioop:音频处理模块,功能已被wave、struct或第三方库(如pydub)覆盖 -
imp:被importlib全面替代,3.12 彻底下线
注意:distutils 的移除影响最广——所有仍依赖 setup.py 直接调用 distutils 的构建脚本,运行时会直接报 ModuleNotFoundError: No module named 'distutils',而非警告。
为什么不用 sys.stdlib_module_names 比较就能知道删了啥?
因为 sys.stdlib_module_names 只返回“当前解释器实际加载的标准库名”,它不包含已被 __all__ = [] 清空、或模块文件虽存在但 import 即失败的“幽灵模块”。比如:
立即学习“Python免费学习笔记(深入)”;
在 3.11 中运行 import distutils 会触发 DeprecationWarning,但 distutils 仍在 sys.stdlib_module_names 里;到了 3.12,distutils 不仅 import 失败,也不再出现在 sys.stdlib_module_names 中——这才是“真删除”的信号。
所以,跨版本比对必须用两个真实环境分别执行:
# 在 Python 3.11 环境中
import sys
import pickle
with open("stdlib_311.pkl", "wb") as f:
pickle.dump(sys.stdlib_module_names, f)
# 在 Python 3.12 环境中
import sys
import pickle
with open("stdlib_311.pkl", "rb") as f:
old = pickle.load(f)
print("新增:", sys.stdlib_module_names - old)
print("移除:", old - sys.stdlib_module_names) # 这里才会显示 distutils、binhex 等
脚本崩溃前,如何快速定位是否用了“死电池”?
不要等上线报错。在迁移到 3.12 前,用以下方式主动扫描:
- 运行
python3.12 -Wall your_script.py:启用全部警告,distutils相关导入会触发ImportWarning(虽然它已不存在,但某些间接引用路径可能残留) - 检查
setup.py或pyproject.toml:若仍有from distutils.core import setup或build_ext,必须替换为setuptools+importlib.metadata - 用
pylint或pyflakes扫描未解析的 import:它们会在静态分析阶段标出distutils、imp等无法 resolve 的模块名
特别注意:有些项目把 distutils 当作“通用工具包”用(比如读取 sys.version_info 或调用 distutils.version.LooseVersion),这类用法必须改用 packaging.version 或 sys.version_info 原生比较。
“死电池”清理不是破坏,而是减负。但它的副作用很实在:所有绕过现代工具链、直啃底层模块的旧脚本,会在 3.12 启动瞬间就卡在 ImportError 上——连日志都来不及打。迁移前务必确认你的依赖树里没有隐式引用这些名字,哪怕只是字符串匹配(比如配置文件里写了 "distutils")。


















