Python3.6中_pickle的C实现不遵循原生类型dispatch_table及lambda序列化问题
解决Python 3.6中_pickle C实现不遵循dispatch_table的问题
我明白你遇到的困扰了——在Python 3.6里,C实现的_pickle._Pickler对原生类型(比如dict、bool、bytes这类)的处理逻辑是硬编码在它的dispatch字典里的,完全不会去读取copyreg注册的dispatch_table,所以你通过copyreg.pickle()注册的自定义序列化函数,对这些原生类型根本起不了作用。
针对这个问题,有两个可行的解决思路:
1. 使用纯Python版的Pickler(推荐)
纯Python实现的pickle.Pickler会严格遵循copyreg的注册规则,不会绕过dispatch_table。你只需要显式使用这个纯Python的Pickler来序列化对象即可:
from six.moves import copyreg import pickle import types # 自定义函数序列化逻辑,这里以lambda为例 def _custom_pickle_func(func): # 捕获函数的核心属性,用于反序列化时重建函数 return (types.FunctionType, (func.__code__, func.__globals__, func.__name__, func.__defaults__, func.__closure__)) # 为FunctionType注册自定义序列化函数 copyreg.pickle(types.FunctionType, _custom_pickle_func) # 用纯Python的Pickler序列化lambda test_lambda = lambda x: x * 3 with open("lambda_dump.pkl", "wb") as f: pickler = pickle.Pickler(f) pickler.dump(test_lambda) # 验证反序列化结果 with open("lambda_dump.pkl", "rb") as f: loaded_lambda = pickle.load(f) print(loaded_lambda(5)) # 输出15,说明序列化成功
这种方法不依赖Python内部私有API,兼容性更强,是更稳妥的选择。
2. 修改C版Pickler的dispatch字典(不推荐)
如果你因为特定需求必须使用C加速的_pickle,可以直接修改它的dispatch字典,替换原生类型的处理函数。但要注意:这属于操作Python的内部私有API,后续Python版本更新很可能会导致代码失效:
import _pickle import pickle # 自定义dict的序列化逻辑 def custom_save_dict(pickler_obj, dict_obj): # 这里可以加入你的自定义处理,比如转换dict结构 modified_dict = {k: str(v) for k, v in dict_obj.items()} # 调用原本的保存方法处理修改后的dict pickler_obj.save(modified_dict) # 替换C版Pickler中dict的处理函数 original_dict_handler = _pickle._Pickler.dispatch[dict] _pickle._Pickler.dispatch[dict] = custom_save_dict # 测试序列化 test_dict = {"a": 1, "b": 2} with open("dict_dump.pkl", "wb") as f: pickle.dump(test_dict, f) # 若有需要,用完后可以还原原来的处理函数 _pickle._Pickler.dispatch[dict] = original_dict_handler
再次提醒:修改内部API风险极高,除非你确定代码只会在特定版本的Python上运行,否则优先选择第一种方法。
内容的提问来源于stack exchange,提问作者Saim Raza
相关产品推荐
相关产品推荐

