当您将内容转储到a中时
pickle,应避免对主模块中声明的类和函数进行酸洗。您的问题是(部分地)因为程序中只有一个文件。
pickle是惰性的,不会序列化类定义或函数定义。相反,它保存了有关如何查找类的参考(类所在的模块及其名称)。
当python直接运行脚本/文件时,它将作为
__main__模块运行程序(无论其实际文件名如何)。但是,当加载文件而 不是
主模块时(例如,当您执行类似操作时
import program),则其模块名称将基于其名称。因此
program.py被称为
program。
从命令行运行时,您正在执行前者,并且该模块称为
__main__。因此,pickle会创建对类的引用
__main__.Signal。当
spyder尝试加载pickle文件时,它会被告知要导入
__main__并查找
Signal。但是,spyder的
__main__模块是用于启动的模块,
spyder而不是您自己的模块,
program.py因此pickle找不到
Signal。
您可以通过运行检查pickle文件的内容(
-a显示每个命令的描述)。由此您将看到您的班级被引用为
__main__.Signal。
python -m pickletools -a file.pkl
您会看到类似以下内容:
0: x80 PROTO 3 Protocol version indicator. 2: c GLOBAL '__main__ Signal' Push a global object (module.attr) on the stack. 19: q BINPUT 0 Store the stack top into the memo. The stack is not popped. 21: ) EMPTY_TUPLE Push an empty tuple. 22: x81 NEWOBJ Build an object instance. 23: q BINPUT 1 Store the stack top into the memo. The stack is not popped. ... 51: b BUILD Finish building an object, via __setstate__ or dict update. 52: . STOP Stop the unpickling machine.highest protocol among oppres = 2
解决方案
有许多解决方案可供您选择:
- 不要序列化
__main__
模块中定义的类的实例。最简单,最好的解决方案。而是将这些类移到另一个模块,或编写main.py
脚本来调用您的程序(这都意味着在__main__
模块中不再找到此类)。 - 编写自定义解串器
- 编写自定义序列化器
以下解决方案将与
out.pkl由以下代码创建的,名为pickle的文件一起使用(在名为的文件中
program.py):
import pickleclass MyClass: def __init__(self, name): self.name = nameif __name__ == '__main__': o = MyClass('test') with open('out.pkl', 'wb') as f: pickle.dump(o, f)定制解串器解决方案
您可以编写一个客户反序列化程序,该程序知道何时遇到对
__main__模块的引用,您真正要指的是
program模块。
import pickleclass MyCustomUnpickler(pickle.Unpickler): def find_class(self, module, name): if module == "__main__": module = "program" return super().find_class(module, name)with open('out.pkl', 'rb') as f: unpickler = MyCustomUnpickler(f) obj = unpickler.load()print(obj)print(obj.name)这是加载已创建的泡菜文件的最简单方法。该程序是将责任推到反序列化代码上,而正确地创建泡菜文件应该真正由序列化代码负责。
定制序列化解决方案
与以前的解决方案相比,您可以确保任何人都可以轻松地对序列化的pickle对象进行反序列化,而无需了解自定义反序列化逻辑。为此,您可以使用
copyreg模块通知
pickle如何反序列化各种类。因此,在这里,您要做的是告诉
pickle所有
__main__类实例反序列化,就好像它们是
program类实例一样。您将需要为每个类注册一个自定义序列化程序
import programimport pickleimport copyregclass MyClass: def __init__(self, name): self.name = namedef pickle_MyClass(obj): assert type(obj) is MyClass return program.MyClass, (obj.name,)copyreg.pickle(MyClass, pickle_MyClass)if __name__ == '__main__': o = MyClass('test') with open('out.pkl', 'wb') as f: pickle.dump(o, f)


