为何无法用pickle反序列化函数?与字典序列化差异解析
为什么用pickle序列化字典对象后能在其他上下文正常加载,但序列化函数时却无法直接加载,除非重新导入该函数?函数对象和字典对象有什么差异?
示例复现
在Notebook A中运行以下代码:
def func(x): return x**2 with open("myfile", "wb") as f: pickle.dump(func,f)
在Notebook B中运行以下代码:
with open("myfile", "rb") as f: func = pickle.load(f)
此时会抛出错误:
AttributeError: Can't get attribute 'func' on <module '__main__'>
核心原因分析
pickle对字典的序列化逻辑:字典是Python内置的基础数据容器,pickle会直接序列化它的全部内容——包括所有键值对的具体数据。加载时,pickle会直接重建出一个和原对象完全一致的新字典,完全不依赖外部上下文。
pickle对函数的序列化逻辑:函数对象不会被pickle序列化具体的代码实现,它只记录函数的元数据:函数名、所属模块名等。加载时,pickle会尝试在当前上下文的对应模块中找到同名的函数对象。你的例子里,Notebook B的
__main__模块中没有定义func,自然找不到就报错了。函数与字典的本质差异:字典的核心是存储的数据,是纯数据载体;而函数是可执行的代码对象,Python中函数的身份由它的定义位置(所属模块+函数名)决定,而非代码本身。这就是为什么重新导入函数后就能加载——此时当前模块中存在了同名的函数对象,pickle可以成功关联。
Notebook中函数的特殊情况:如果两个Notebook运行在同一个Python进程,或者函数被定义在独立的模块文件(而非
__main__),可能能正常加载,但Notebook默认的__main__模块是相互独立的,所以通常还是会遇到相同问题。解决办法是把函数放到单独的.py模块中,在两个Notebook里都导入该模块,再进行序列化和加载操作。
内容的提问来源于stack exchange,提问作者Kaoutar

