通过重新编译脚本更新类定义后,pickle拒绝序列化该类的先前实例化对象,给出错误:“不能pickle对象:它不是与”
相同的对象有没有办法告诉pickle它应该忽略这种情况?要仅按名称标识类,请忽略导致不匹配的内部唯一ID?
我绝对欢迎作为答案提出一个替代的,等效模块的建议,该模块以方便和稳健的方式解决了这个问题。
供参考,这是我的动机:
我正在创建一个高效,快速的迭代开发环境,其中Python脚本是实时编辑的。脚本被反复重新编译,但数据在编译中仍然存在。作为生产力目标的一部分,我正在尝试使用pickle进行序列化,以避免为不断变化的数据结构编写和更新显式序列化代码的成本。
主要是我序列化内置类型。我小心避免在我挑选的类中进行有意义的更改,并在必要时使用copy_reg.pickle机制在unpickle上执行upconversion。
脚本重新编译使我完全不会腌制对象,即使类定义实际上没有改变(或者只是以良性方式改变)。
答案 0 :(得分:8)
除非您可以解包早期版本的类定义,否则引用pickle需要转储并加载实例现在已经消失。所以这是“不可能的”。
但是,如果您确实想要这样做,您可以保存以前版本的类定义......然后就是你必须欺骗pickle来引用你的旧的/已保存的类定义,而不是使用最新的类定义 - 这可能仅等于编辑obj.__class__
或obj.__module__
以指向您的旧类。您的类实例中可能还有一些其他奇怪的东西也引用了您必须处理的旧类定义。此外,如果添加或删除类方法,则可能会遇到一些意外结果,或者必须相应地更新实例。另一个有趣的转折是你可以让unpickler总是使用你班级的最新版本。
我的序列化包dill有一些方法可以将已编译的源从实时代码对象转储到临时文件,然后使用该临时文件进行序列化。它是包装中较新的部分之一,因此它不如莳萝的其他部分坚固。此外,您的用例不是我考虑的用例,但我可以看到它是一个很好的功能。
答案 1 :(得分:0)
我想到了两个解决方案:
你可以设置object.__class__
>>> class X(object):
pass
>>> class Y(object):
pass
>>> x = X()
>>> x.__class__ = Y
>>> type(x)
<class '__main__.Y'>
也许您可以使用persistent_id
,因为每个对象都会传递给它。
定义__reduce__
以完成与pickle相同的操作。 (看看pickle.py)
答案 2 :(得分:0)
有一种简单的方法,基本上是User's answer。
首先,我将给出失败的代码:
#Tested with Python 3.6.7
import pickle
class Foo:
pass
foo = Foo()
class Foo:
def bar(self):
return 0
pickle.dumps(foo) #raises PicklingError: Can't pickle <class '__main__.Foo'>: it's not the same object as __main__.Foo
要解决此问题,只需在酸洗之前重置__class__
的{{1}}属性即可,如用户回答:
foo
仅当您确实希望泡菜忽略类的两个版本之间的任何差异时,此解决方案才有效。如果两个版本之间存在重大差异,那么我认为此解决方案不会起作用。