如何对包含ct.Pointer的ctypes.Structure进行Pickling序列化?
解决包含指针的ctypes结构体Pickle序列化问题
你已经迈出了关键一步——给my_array实现了__reduce__方法来处理指针数据的序列化,接下来我们把这个思路扩展到包含这类结构体的my_struct上,完整实现pickle支持。
先修正并确认my_array的实现
首先注意你的my_array里用了xrange,这是Python2的语法,Python3里要换成range,先修正一下:
import ctypes as ct import pickle class my_array(ct.Structure): _fields_ = [("_data", ct.POINTER(ct.c_uint32)), ("_size", ct.c_size_t)] def __init__(self, data): # 构建ctypes数组并赋值 self._data = (ct.c_uint32 * len(data))() for i in range(len(data)): self._data[i] = data[i] self._size = len(data) def __reduce__(self): # 把指针指向的数据提取成Python列表 data = [self._data[j] for j in range(self._size)] # 返回构造函数和对应的参数,pickle会用这个重建对象 return (my_array, (data,))
实现my_struct的Pickle支持
假设你的my_struct字段是包含一个my_array类型的numbs(如果有其他字段,思路完全一致),我们同样通过__reduce__来拆解和重建对象:
class my_struct(ct.Structure): _fields_ = [("numbs", my_array)] def __init__(self, numbs_data): # 初始化时直接传入Python列表,内部自动构建my_array self.numbs = my_array(numbs_data) def __reduce__(self): # 提取my_struct里的可序列化核心数据:这里就是numbs对应的Python列表 # 其实因为my_array已经实现了__reduce__,也可以直接传self.numbs,但提取底层数据更直观 numbs_data = [self.numbs._data[j] for j in range(self.numbs._size)] # 返回构造函数和参数,pickle会用这个重建my_struct实例 return (my_struct, (numbs_data,))
测试序列化和反序列化
现在来验证一下是否能正常工作:
# 创建测试对象 test_struct = my_struct([1, 2, 3, 4, 5]) # 序列化到字节流 pickled_data = pickle.dumps(test_struct) # 反序列化重建对象 loaded_struct = pickle.loads(pickled_data) # 验证数据一致性 print("原始数据:", [test_struct.numbs._data[i] for i in range(test_struct.numbs._size)]) print("反序列化后数据:", [loaded_struct.numbs._data[i] for i in range(loaded_struct.numbs._size)])
关键要点总结
__reduce__是pickle序列化的核心,它告诉pickle如何将复杂对象拆解成可序列化的原生Python类型,以及如何用这些数据重建对象- 对于ctypes指针,绝对不能直接序列化指针本身(指针是内存地址,反序列化后毫无意义),必须提取指针指向的实际数据,转换成列表、元组等原生类型
- 如果
my_struct有多个字段,只要每个字段都能被pickle序列化(要么是原生类型,要么自己实现了__reduce__),就可以在__reduce__里把所有字段的数据提取出来,作为构造函数的参数返回
内容的提问来源于stack exchange,提问作者Tohiko
相关产品推荐
相关产品推荐

