自定义pickle行为以实现向后兼容性

时间:2014-09-04 09:31:16

标签: python persistence pickle

Python的copy_reg模块允许注册自定义Reducer和构造函数。是否正确我只能自定义之后序列化的对象的非线性行为我通过copy_reg.pickle注册了自定义序列化器/反序列化器?

示例:

import pickle, copy_reg

class C(object):
    pass

legacy_c_ser = pickle.dumps(C())

def reduce_C(obj):
    print('reduce_C called')
    tpl = obj.__reduce__()
    tpl = (load_C, ) + tpl[1:]
    return tpl

def load_C(*tpl):
    print('load_C called')
    return C()

copy_reg.constructor(load_C)
copy_reg.pickle(C, reduce_C, load_C)

new_c_ser = pickle.dumps(C())

# load_C is called
pickle.loads(new_c_ser)

# load_C is not called
pickle.loads(legacy_c_ser)

似乎copy_reg只是简单地用pickle格式替换构造函数:

>>> print(legacy_c_ser)
'ccopy_reg\n_reconstructor\np0\n(c__main__\nC\np1\[...]'
>>> print(new_c_ser)
'c__main__\nload_C\np0[...]'

编写我自己的pickle.Unpickler类是自定义传统pickle文件的unpickle行为的唯一方法吗?我不想这样做,因为效率高于cPickle而不是pickle

我的问题是我从第三方库中腌制了对象,并且在升级库时改变了pickle格式。

1 个答案:

答案 0 :(得分:1)

Pickle应该在python版本中向后兼容(现在让我们忽略python 2.x与3.x)。因此,当您说酸洗格式发生变化时,您的意思是第三方库注册其类(或其他对象)的方式已更改,是否正确?

如果是这样的......要做到这一点,你需要做一些诡计。首先,您获得旧类定义的源代码,当您获取原始pickle时,您需要更改现有类的引用,以匹配旧版本类的代码路径。这应该是明文(即使在HIGHEST_PROTOCOL),所以抓取和编辑pickle字符串的那一部分应该不是问题。然后你就可以解开旧对象,但是他们会指向旧的类定义。将您要取消的旧对象转换为新的类实例对象需要“格式转换器” - 基本上创建从旧类实例中获取相关状态的新类实例。