多层Ctype结构转YAML时内部结构序列化异常问题
问题描述
我用Ctypes定义了嵌套的Structure结构,将其转为Python字典后导出YAML时,内部子结构的值被序列化为Python对象格式(!!python/object/apply:_ctypes._unpickle),但直接打印该子结构的字典表示是正常的。硬编码相同内容的字典导出YAML则完全正常。
环境:Python 3.10,PyYAML 6.0
核心代码逻辑:
- 自定义
My_Structure继承自ctypes.Structure,实现as_dict()方法递归转换数组,但未处理嵌套的Structure实例 - 嵌套结构
substruct_t作为ee_struct的字段 - 通过
eeprom_tUnion的get_as_dict()获取字典后用PyYAML导出
原因分析
问题出在My_Structure的__recursive_carray_get方法:该方法只判断了数组类型(通过__len__属性),但嵌套的substruct_t实例是My_Structure对象,不会触发递归转换。当调用getattr(self, field[0])获取substruct字段时,得到的是substruct_t实例而非其字典表示,PyYAML会默认将ctypes对象序列化为pickle格式,导致YAML中出现!!python/object/apply内容。
虽然打印as_dict()时看起来是字典,但实际上value对应的是substruct_t实例,只是打印时调用了__repr__显示为字典,而PyYAML序列化时会直接处理底层对象。
解决方案
修改__recursive_carray_get方法,增加对My_Structure实例的判断,递归调用其as_dict()方法;同时将ctypes基础类型转换为Python原生类型,避免残留ctypes对象:
class My_Structure(Structure): def __recursive_carray_get(self, value): # 优先处理嵌套的Structure实例 if isinstance(value, My_Structure): return value.as_dict() # 处理数组类型 elif hasattr(value, '__len__'): rtn = list() for i in range(value.__len__()): rtn.append(self.__recursive_carray_get(value.__getitem__(i))) return rtn # 处理ctypes基础类型(位域、数值等),转为Python原生类型 else: return value.value if hasattr(value, 'value') else value def __handle_array_type__(self, type): return StringBetween("'", "'", str(type)).split(".")[1] def __repr__(self) -> str: return str(self.as_dict()) def __str__(self) -> str: values = ",\n".join(f"{name}={value['value']}" for name, value in self.as_dict().items()) return f"<{self.__class__.__name__}: {values}>" def as_dict(self) -> dict: return { field[0]: { 'value': self.__recursive_carray_get(getattr(self, field[0])), 'type': self.__handle_array_type__(field[1]) } for field in self._fields_ }
验证效果
修改后,substruct字段的value会被正确转换为嵌套字典,PyYAML导出时会正常序列化为普通字典结构,不再出现pickle格式的对象内容。
内容的提问来源于stack exchange,提问作者István Szekér

