Python加载npz文件时在numpy代码段报旧模块ModuleNotFoundError
问题背景
你正在开发Python包hsltools,当前使用模块为em_combiner1_4,近期刚完成从em_combiner1_3版本的升级,已手动修改代码中所有显式导入语句,但运行测试代码时抛出如下错误:
Traceback (most recent call last): File "C:\Users\...\git_home\hsltools\hsltools\tests\emc_tester\EMC_tester.py", line 85, in <module> combine_2d() File "C:\Users\...\git_home\hsltools\hsltools\tests\emc_tester\EMC_tester.py", line 48, in combine_2d elec_field2d = emc.EM(import_path+"2DelecMap.ef2.npz") File "c:\users\...\git_home\hsltools\hsltools\em_combiner1_4.py", line 441, in __init__ load_npz() File "c:\users\...\git_home\hsltools\hsltools\em_combiner1_4.py", line 251, in load_npz self.em_type = data["metadata"][1] File "C:\Users\...\Anaconda3\lib\site-packages\numpy\lib\npyio.py", line 254, in __getitem__ return format.read_array(bytes, File "C:\Users\...\Anaconda3\lib\site-packages\numpy\lib\format.py", line 744, in read_array array = pickle.load(fp, **pickle_kwargs) ModuleNotFoundError: No module named 'hsltools.em_combiner1_3'
你对报错来源的判断存在认知偏差,报错定位本身没有异常。
根因分析
- numpy存储包含自定义Python对象的数组时,底层依赖
pickle做序列化,会完整记录对象所属类的全量导入路径;反序列化加载文件时,pickle会自动按照记录的路径导入对应模块、还原对象,这个过程对调用方透明,不会在numpy源码里体现显式导入你业务模块的代码。 - 你当前加载的
2DelecMap.ef2.npz文件,是升级前用em_combiner1_3版本生成的,文件内序列化记录的类路径为hsltools.em_combiner1_3。升级后你把模块文件名改成了em_combiner1_4,旧路径对应的模块不存在,pickle反序列化时就会抛出找不到模块的错误。 - 报错栈最终落在numpy的
format.py文件,只是因为pickle的反序列化逻辑是被numpy的读文件方法调用的,不是numpy本身依赖你的业务模块。
解决方案
根据你的使用场景选对应方案即可:
- 临时快速修复:在执行业务逻辑、加载npz文件之前,手动给旧模块路径加别名映射,让Python解释器能把旧路径指向新模块,示例代码:
import sys from hsltools import em_combiner1_4 # 注册旧模块路径到新模块的映射 sys.modules["hsltools.em_combiner1_3"] = em_combiner1_4
添加上述代码后再加载旧版生成的npz文件,pickle就能正常完成反序列化。
- 长期修复方案1:用升级后的
em_combiner1_4模块重新生成所有需要用到的npz数据文件,新生成的文件会写入新的模块路径,后续加载不会再触发该错误。 - 长期修复方案2:如果需要兼容历史版本生成的所有npz文件,可以把上述模块映射逻辑写到
hsltools包的__init__.py文件中,包导入时自动完成映射,不需要使用者额外处理。
内容的提问来源于stack exchange,提问作者Jonathanthesailor
相关产品推荐
相关产品推荐

