Python ctypes进阶如何优雅地封装一个C类供Python调用含结构体与指针处理在数据科学和系统编程的交汇处我们常常需要将高性能的C计算引擎与Python的灵活生态相结合。当简单的C函数调用无法满足需求时如何用Python的ctypes模块完整封装一个包含类、结构体和复杂指针操作的C库就成为了一个值得深入探讨的话题。想象这样一个场景你的团队开发了一个用C编写的数据处理引擎其中包含多个相互关联的类、自定义结构体以及涉及内存管理的指针操作。现在需要将这个引擎完整地暴露给Python调用同时保持面向对象的编程风格和类型安全。这正是ctypes在进阶使用中最能展现其价值的地方。1. C类封装的整体架构设计封装C类到Python本质上是在两种语言间建立桥梁。由于ctypes本身只支持C风格的函数调用我们需要设计一个中间层来模拟面向对象的行为。这个设计包含三个关键部分C接口层用extern C包装C类方法内存管理层处理对象生命周期和this指针Python包装层创建符合Python习惯的面向对象接口一个典型的C类封装架构如下// DataEngine.h class DataEngine { public: DataEngine(int init_param); ~DataEngine(); struct DataPacket { int id; double* values; size_t length; }; DataPacket process(const DataPacket input); void configure(const std::string params); private: // 私有成员和方法... };对应的C接口层应该这样设计extern C { void* DataEngine_new(int init_param) { return new DataEngine(init_param); } void DataEngine_delete(void* engine) { delete static_castDataEngine*(engine); } // 其他成员函数的C风格包装... }2. 处理this指针和对象生命周期在Python中模拟C类的关键是要正确处理this指针。ctypes没有内置的类支持我们需要手动管理对象实例和成员函数调用。2.1 对象创建与销毁首先定义Python端的类结构import ctypes class DataEngine: def __init__(self, init_param): # 加载动态库 self._lib ctypes.CDLL(./data_engine.dll) # 设置函数原型 self._lib.DataEngine_new.argtypes [ctypes.c_int] self._lib.DataEngine_new.restype ctypes.c_void_p self._lib.DataEngine_delete.argtypes [ctypes.c_void_p] # 创建C对象 self._obj self._lib.DataEngine_new(init_param) def __del__(self): if hasattr(self, _obj) and self._obj: self._lib.DataEngine_delete(self._obj)这种模式确保了Python对象销毁时对应的C对象也会被正确清理。注意以下几点c_void_p用于表示C对象的指针析构函数应该处理对象可能为NULL的情况建议添加错误检查确保动态库加载成功2.2 成员函数封装封装成员函数需要将this指针作为第一个参数传递。考虑以下C成员函数void DataEngine::configure(const std::string params);对应的C接口和Python封装应该是// C接口 extern C void DataEngine_configure(void* engine, const char* params) { static_castDataEngine*(engine)-configure(params); }# Python封装 class DataEngine: # ... 前面的初始化代码 def configure(self, params): self._lib.DataEngine_configure.argtypes [ ctypes.c_void_p, ctypes.c_char_p ] # 确保字符串编码正确 if isinstance(params, str): params params.encode(utf-8) self._lib.DataEngine_configure(self._obj, params)3. 结构体的双向传递当C类使用自定义结构体作为参数或返回值时需要在Python中定义对应的ctypes结构体。以DataPacket为例3.1 定义等效的ctypes结构体class DataPacket(ctypes.Structure): _fields_ [ (id, ctypes.c_int), (values, ctypes.POINTER(ctypes.c_double)), (length, ctypes.c_size_t) ]3.2 处理结构体作为参数的情况对于C函数DataPacket DataEngine::process(const DataPacket input);我们需要考虑结构体按值传递和按引用传递的不同处理方式# 在DataEngine类中添加方法 def process(self, input_packet): # 设置函数原型 self._lib.DataEngine_process.argtypes [ ctypes.c_void_p, ctypes.POINTER(DataPacket) # 传递结构体指针 ] self._lib.DataEngine_process.restype DataPacket # 准备输入结构体 input_ptr ctypes.pointer(input_packet) # 调用并返回结果 return self._lib.DataEngine_process(self._obj, input_ptr)3.3 处理结构体内的指针DataPacket中的values指针需要特别注意内存管理。在Python端创建和销毁这类结构体时def create_data_packet(packet_id, values): # 转换Python列表到C数组 values_arr (ctypes.c_double * len(values))(*values) # 创建结构体实例 packet DataPacket() packet.id packet_id packet.values values_arr packet.length len(values) return packet # 使用示例 values [1.1, 2.2, 3.3] packet create_data_packet(1, values) result engine.process(packet)4. 高级指针操作与内存管理C类中经常涉及复杂的指针操作在Python端需要谨慎处理以避免内存错误。4.1 返回指针的处理考虑一个返回内部数据指针的成员函数const double* DataEngine::get_results() const;在Python端的封装应该考虑指针的生命周期数据的安全访问防止内存泄漏# 在DataEngine类中添加 def get_results(self, lengthNone): self._lib.DataEngine_get_results.argtypes [ctypes.c_void_p] self._lib.DataEngine_get_results.restype ctypes.POINTER(ctypes.c_double) # 获取指针 ptr self._lib.DataEngine_get_results(self._obj) if length is None: # 需要其他方式获取长度 length self.get_result_length() # 将指针转换为Python可访问的数组 return ptr[:length] if ptr else None4.2 回调函数与函数指针当C类需要回调函数时ctypes也能很好地处理typedef void (*Callback)(int progress, void* user_data); void DataEngine::set_progress_callback(Callback cb, void* user_data);Python端的实现# 定义回调类型 CALLBACK_TYPE ctypes.CFUNCTYPE(None, ctypes.c_int, ctypes.c_void_p) # 在DataEngine类中 def set_progress_callback(self, py_callback): # 保持回调引用避免被GC self._callback CALLBACK_TYPE(py_callback) self._lib.DataEngine_set_progress_callback.argtypes [ ctypes.c_void_p, CALLBACK_TYPE, ctypes.c_void_p ] # 传递Python对象作为user_data user_data ctypes.py_object(self) self._lib.DataEngine_set_progress_callback( self._obj, self._callback, ctypes.cast(user_data, ctypes.c_void_p) )5. 错误处理与异常安全将C异常传递到Python需要特殊处理。一个常见的模式是C端捕获异常并返回错误码Python端检查错误码并抛出异常extern C int DataEngine_safe_operation(void* engine, const char* input) { try { static_castDataEngine*(engine)-unsafe_operation(input); return 0; // 成功 } catch (const std::exception e) { // 可以记录错误信息 return -1; // 失败 } }Python端的封装def safe_operation(self, input_str): self._lib.DataEngine_safe_operation.argtypes [ ctypes.c_void_p, ctypes.c_char_p ] self._lib.DataEngine_safe_operation.restype ctypes.c_int result self._lib.DataEngine_safe_operation( self._obj, input_str.encode(utf-8) ) if result ! 0: raise RuntimeError(Operation failed in C engine)6. 性能优化技巧ctypes调用有一定的开销以下方法可以提升性能批量操作尽量减少跨语言调用次数内存视图使用memoryview共享大数据而非复制类型缓存缓存函数原型设置结果class DataEngine: def __init__(self, init_param): # ... 其他初始化 # 预先设置常用函数的原型 self._setup_function_prototypes() def _setup_function_prototypes(self): 预先设置所有函数的原型避免重复设置的开销 self._lib.DataEngine_process.argtypes [ ctypes.c_void_p, ctypes.POINTER(DataPacket) ] self._lib.DataEngine_process.restype DataPacket # 设置其他函数的原型...对于频繁调用的简单函数可以考虑使用ctypes.CDLL的_FuncPtr特性直接访问函数减少Python层的开销。7. 实际案例完整封装一个数据处理引擎让我们综合以上技术完整封装一个假设的DataEngine类。这个引擎具有以下功能创建/销毁引擎实例处理包含数组的数据包支持进度回调错误处理7.1 C头文件 (DataEngine.h)#include string #include functional class DataEngine { public: using Callback std::functionvoid(int); DataEngine(int init_param); ~DataEngine(); struct DataPacket { int id; double* data; size_t size; }; void configure(const std::string config); DataPacket process(const DataPacket input); void set_callback(Callback cb); private: int param_; Callback callback_; };7.2 C接口层 (DataEngineWrapper.cpp)#include DataEngine.h #include cstring extern C { // 对象生命周期 void* DataEngine_new(int param) { return new DataEngine(param); } void DataEngine_delete(void* engine) { delete static_castDataEngine*(engine); } // 配置 void DataEngine_configure(void* engine, const char* config) { static_castDataEngine*(engine)-configure(config); } // 数据处理 void DataEngine_process( void* engine, const DataEngine::DataPacket* input, DataEngine::DataPacket* output ) { *output static_castDataEngine*(engine)-process(*input); } // 回调设置 void DataEngine_set_callback(void* engine, void (*cb)(int, void*), void* user_data) { static_castDataEngine*(engine)-set_callback( [cb, user_data](int progress) { cb(progress, user_data); } ); } }7.3 Python封装 (data_engine.py)import ctypes import sys from typing import Optional, List, Callable class DataPacket(ctypes.Structure): _fields_ [ (id, ctypes.c_int), (data, ctypes.POINTER(ctypes.c_double)), (size, ctypes.c_size_t) ] def to_python(self) - dict: 将C结构体转换为Python字典 return { id: self.id, data: self.data[:self.size] if self.data else [], size: self.size } classmethod def from_python(cls, packet_dict: dict) - DataPacket: 从Python字典创建C结构体 packet cls() packet.id packet_dict[id] packet.size len(packet_dict[data]) # 分配数组内存 if packet.size 0: arr_type ctypes.c_double * packet.size packet.data ctypes.cast(arr_type(*packet_dict[data]), ctypes.POINTER(ctypes.c_double)) else: packet.data None return packet class DataEngine: def __init__(self, init_param: int, lib_path: Optional[str] None): 初始化数据处理引擎 Args: init_param: 初始化参数 lib_path: 可选动态库路径。如果为None尝试默认路径查找 # 尝试加载动态库 self._lib self._load_library(lib_path) # 设置函数原型 self._setup_function_prototypes() # 创建C对象 self._obj self._lib.DataEngine_new(init_param) if not self._obj: raise RuntimeError(Failed to create DataEngine instance) # 回调相关 self._callback None self._user_data None def _load_library(self, lib_path: Optional[str]) - ctypes.CDLL: 加载动态库 if lib_path is None: # 尝试默认路径 lib_path self._find_default_library() try: return ctypes.CDLL(lib_path) except Exception as e: raise RuntimeError(fFailed to load library: {e}) def _setup_function_prototypes(self): 设置所有C函数的原型 # 对象生命周期 self._lib.DataEngine_new.argtypes [ctypes.c_int] self._lib.DataEngine_new.restype ctypes.c_void_p self._lib.DataEngine_delete.argtypes [ctypes.c_void_p] # 配置 self._lib.DataEngine_configure.argtypes [ ctypes.c_void_p, ctypes.c_char_p ] # 数据处理 self._lib.DataEngine_process.argtypes [ ctypes.c_void_p, ctypes.POINTER(DataPacket), ctypes.POINTER(DataPacket) ] # 回调 self._callback_type ctypes.CFUNCTYPE(None, ctypes.c_int, ctypes.c_void_p) self._lib.DataEngine_set_callback.argtypes [ ctypes.c_void_p, self._callback_type, ctypes.c_void_p ] def configure(self, config: str): 配置引擎 if not isinstance(config, bytes): config config.encode(utf-8) self._lib.DataEngine_configure(self._obj, config) def process(self, input_data: dict) - dict: 处理数据包 Args: input_data: 包含id和data字段的字典 Returns: 处理后的数据包字典 # 准备输入 input_packet DataPacket.from_python(input_data) # 准备输出 output_packet DataPacket() # 调用处理函数 self._lib.DataEngine_process( self._obj, ctypes.byref(input_packet), ctypes.byref(output_packet) ) # 转换为Python字典 result output_packet.to_python() # 清理资源 if input_packet.data: ctypes.free(input_packet.data) return result def set_callback(self, callback: Callable[[int], None]): 设置进度回调函数 Args: callback: 接受一个int参数(进度百分比)的函数 if callback is None: # 清除回调 self._lib.DataEngine_set_callback(self._obj, None, None) self._callback None self._user_data None return # 包装Python回调 def wrapped_callback(progress: int, _: ctypes.c_void_p): callback(progress) # 保持回调引用 self._callback self._callback_type(wrapped_callback) self._user_data ctypes.py_object(self) # 设置回调 self._lib.DataEngine_set_callback( self._obj, self._callback, ctypes.cast(self._user_data, ctypes.c_void_p) ) def __del__(self): 析构函数确保C对象被正确释放 if hasattr(self, _obj) and self._obj: self._lib.DataEngine_delete(self._obj) self._obj None staticmethod def _find_default_library() - str: 尝试在不同平台上查找默认库路径 if sys.platform win32: return DataEngine.dll elif sys.platform darwin: return libDataEngine.dylib else: return libDataEngine.so这个完整的封装示例展示了如何将面向对象的C库优雅地暴露给Python使用同时处理了内存管理、类型转换、回调函数等复杂情况。在实际项目中你可能还需要添加日志、更详细的错误检查和线程安全措施。
Python ctypes进阶:如何优雅地封装一个C++类供Python调用(含结构体与指针处理)
发布时间:2026/6/6 3:36:16
Python ctypes进阶如何优雅地封装一个C类供Python调用含结构体与指针处理在数据科学和系统编程的交汇处我们常常需要将高性能的C计算引擎与Python的灵活生态相结合。当简单的C函数调用无法满足需求时如何用Python的ctypes模块完整封装一个包含类、结构体和复杂指针操作的C库就成为了一个值得深入探讨的话题。想象这样一个场景你的团队开发了一个用C编写的数据处理引擎其中包含多个相互关联的类、自定义结构体以及涉及内存管理的指针操作。现在需要将这个引擎完整地暴露给Python调用同时保持面向对象的编程风格和类型安全。这正是ctypes在进阶使用中最能展现其价值的地方。1. C类封装的整体架构设计封装C类到Python本质上是在两种语言间建立桥梁。由于ctypes本身只支持C风格的函数调用我们需要设计一个中间层来模拟面向对象的行为。这个设计包含三个关键部分C接口层用extern C包装C类方法内存管理层处理对象生命周期和this指针Python包装层创建符合Python习惯的面向对象接口一个典型的C类封装架构如下// DataEngine.h class DataEngine { public: DataEngine(int init_param); ~DataEngine(); struct DataPacket { int id; double* values; size_t length; }; DataPacket process(const DataPacket input); void configure(const std::string params); private: // 私有成员和方法... };对应的C接口层应该这样设计extern C { void* DataEngine_new(int init_param) { return new DataEngine(init_param); } void DataEngine_delete(void* engine) { delete static_castDataEngine*(engine); } // 其他成员函数的C风格包装... }2. 处理this指针和对象生命周期在Python中模拟C类的关键是要正确处理this指针。ctypes没有内置的类支持我们需要手动管理对象实例和成员函数调用。2.1 对象创建与销毁首先定义Python端的类结构import ctypes class DataEngine: def __init__(self, init_param): # 加载动态库 self._lib ctypes.CDLL(./data_engine.dll) # 设置函数原型 self._lib.DataEngine_new.argtypes [ctypes.c_int] self._lib.DataEngine_new.restype ctypes.c_void_p self._lib.DataEngine_delete.argtypes [ctypes.c_void_p] # 创建C对象 self._obj self._lib.DataEngine_new(init_param) def __del__(self): if hasattr(self, _obj) and self._obj: self._lib.DataEngine_delete(self._obj)这种模式确保了Python对象销毁时对应的C对象也会被正确清理。注意以下几点c_void_p用于表示C对象的指针析构函数应该处理对象可能为NULL的情况建议添加错误检查确保动态库加载成功2.2 成员函数封装封装成员函数需要将this指针作为第一个参数传递。考虑以下C成员函数void DataEngine::configure(const std::string params);对应的C接口和Python封装应该是// C接口 extern C void DataEngine_configure(void* engine, const char* params) { static_castDataEngine*(engine)-configure(params); }# Python封装 class DataEngine: # ... 前面的初始化代码 def configure(self, params): self._lib.DataEngine_configure.argtypes [ ctypes.c_void_p, ctypes.c_char_p ] # 确保字符串编码正确 if isinstance(params, str): params params.encode(utf-8) self._lib.DataEngine_configure(self._obj, params)3. 结构体的双向传递当C类使用自定义结构体作为参数或返回值时需要在Python中定义对应的ctypes结构体。以DataPacket为例3.1 定义等效的ctypes结构体class DataPacket(ctypes.Structure): _fields_ [ (id, ctypes.c_int), (values, ctypes.POINTER(ctypes.c_double)), (length, ctypes.c_size_t) ]3.2 处理结构体作为参数的情况对于C函数DataPacket DataEngine::process(const DataPacket input);我们需要考虑结构体按值传递和按引用传递的不同处理方式# 在DataEngine类中添加方法 def process(self, input_packet): # 设置函数原型 self._lib.DataEngine_process.argtypes [ ctypes.c_void_p, ctypes.POINTER(DataPacket) # 传递结构体指针 ] self._lib.DataEngine_process.restype DataPacket # 准备输入结构体 input_ptr ctypes.pointer(input_packet) # 调用并返回结果 return self._lib.DataEngine_process(self._obj, input_ptr)3.3 处理结构体内的指针DataPacket中的values指针需要特别注意内存管理。在Python端创建和销毁这类结构体时def create_data_packet(packet_id, values): # 转换Python列表到C数组 values_arr (ctypes.c_double * len(values))(*values) # 创建结构体实例 packet DataPacket() packet.id packet_id packet.values values_arr packet.length len(values) return packet # 使用示例 values [1.1, 2.2, 3.3] packet create_data_packet(1, values) result engine.process(packet)4. 高级指针操作与内存管理C类中经常涉及复杂的指针操作在Python端需要谨慎处理以避免内存错误。4.1 返回指针的处理考虑一个返回内部数据指针的成员函数const double* DataEngine::get_results() const;在Python端的封装应该考虑指针的生命周期数据的安全访问防止内存泄漏# 在DataEngine类中添加 def get_results(self, lengthNone): self._lib.DataEngine_get_results.argtypes [ctypes.c_void_p] self._lib.DataEngine_get_results.restype ctypes.POINTER(ctypes.c_double) # 获取指针 ptr self._lib.DataEngine_get_results(self._obj) if length is None: # 需要其他方式获取长度 length self.get_result_length() # 将指针转换为Python可访问的数组 return ptr[:length] if ptr else None4.2 回调函数与函数指针当C类需要回调函数时ctypes也能很好地处理typedef void (*Callback)(int progress, void* user_data); void DataEngine::set_progress_callback(Callback cb, void* user_data);Python端的实现# 定义回调类型 CALLBACK_TYPE ctypes.CFUNCTYPE(None, ctypes.c_int, ctypes.c_void_p) # 在DataEngine类中 def set_progress_callback(self, py_callback): # 保持回调引用避免被GC self._callback CALLBACK_TYPE(py_callback) self._lib.DataEngine_set_progress_callback.argtypes [ ctypes.c_void_p, CALLBACK_TYPE, ctypes.c_void_p ] # 传递Python对象作为user_data user_data ctypes.py_object(self) self._lib.DataEngine_set_progress_callback( self._obj, self._callback, ctypes.cast(user_data, ctypes.c_void_p) )5. 错误处理与异常安全将C异常传递到Python需要特殊处理。一个常见的模式是C端捕获异常并返回错误码Python端检查错误码并抛出异常extern C int DataEngine_safe_operation(void* engine, const char* input) { try { static_castDataEngine*(engine)-unsafe_operation(input); return 0; // 成功 } catch (const std::exception e) { // 可以记录错误信息 return -1; // 失败 } }Python端的封装def safe_operation(self, input_str): self._lib.DataEngine_safe_operation.argtypes [ ctypes.c_void_p, ctypes.c_char_p ] self._lib.DataEngine_safe_operation.restype ctypes.c_int result self._lib.DataEngine_safe_operation( self._obj, input_str.encode(utf-8) ) if result ! 0: raise RuntimeError(Operation failed in C engine)6. 性能优化技巧ctypes调用有一定的开销以下方法可以提升性能批量操作尽量减少跨语言调用次数内存视图使用memoryview共享大数据而非复制类型缓存缓存函数原型设置结果class DataEngine: def __init__(self, init_param): # ... 其他初始化 # 预先设置常用函数的原型 self._setup_function_prototypes() def _setup_function_prototypes(self): 预先设置所有函数的原型避免重复设置的开销 self._lib.DataEngine_process.argtypes [ ctypes.c_void_p, ctypes.POINTER(DataPacket) ] self._lib.DataEngine_process.restype DataPacket # 设置其他函数的原型...对于频繁调用的简单函数可以考虑使用ctypes.CDLL的_FuncPtr特性直接访问函数减少Python层的开销。7. 实际案例完整封装一个数据处理引擎让我们综合以上技术完整封装一个假设的DataEngine类。这个引擎具有以下功能创建/销毁引擎实例处理包含数组的数据包支持进度回调错误处理7.1 C头文件 (DataEngine.h)#include string #include functional class DataEngine { public: using Callback std::functionvoid(int); DataEngine(int init_param); ~DataEngine(); struct DataPacket { int id; double* data; size_t size; }; void configure(const std::string config); DataPacket process(const DataPacket input); void set_callback(Callback cb); private: int param_; Callback callback_; };7.2 C接口层 (DataEngineWrapper.cpp)#include DataEngine.h #include cstring extern C { // 对象生命周期 void* DataEngine_new(int param) { return new DataEngine(param); } void DataEngine_delete(void* engine) { delete static_castDataEngine*(engine); } // 配置 void DataEngine_configure(void* engine, const char* config) { static_castDataEngine*(engine)-configure(config); } // 数据处理 void DataEngine_process( void* engine, const DataEngine::DataPacket* input, DataEngine::DataPacket* output ) { *output static_castDataEngine*(engine)-process(*input); } // 回调设置 void DataEngine_set_callback(void* engine, void (*cb)(int, void*), void* user_data) { static_castDataEngine*(engine)-set_callback( [cb, user_data](int progress) { cb(progress, user_data); } ); } }7.3 Python封装 (data_engine.py)import ctypes import sys from typing import Optional, List, Callable class DataPacket(ctypes.Structure): _fields_ [ (id, ctypes.c_int), (data, ctypes.POINTER(ctypes.c_double)), (size, ctypes.c_size_t) ] def to_python(self) - dict: 将C结构体转换为Python字典 return { id: self.id, data: self.data[:self.size] if self.data else [], size: self.size } classmethod def from_python(cls, packet_dict: dict) - DataPacket: 从Python字典创建C结构体 packet cls() packet.id packet_dict[id] packet.size len(packet_dict[data]) # 分配数组内存 if packet.size 0: arr_type ctypes.c_double * packet.size packet.data ctypes.cast(arr_type(*packet_dict[data]), ctypes.POINTER(ctypes.c_double)) else: packet.data None return packet class DataEngine: def __init__(self, init_param: int, lib_path: Optional[str] None): 初始化数据处理引擎 Args: init_param: 初始化参数 lib_path: 可选动态库路径。如果为None尝试默认路径查找 # 尝试加载动态库 self._lib self._load_library(lib_path) # 设置函数原型 self._setup_function_prototypes() # 创建C对象 self._obj self._lib.DataEngine_new(init_param) if not self._obj: raise RuntimeError(Failed to create DataEngine instance) # 回调相关 self._callback None self._user_data None def _load_library(self, lib_path: Optional[str]) - ctypes.CDLL: 加载动态库 if lib_path is None: # 尝试默认路径 lib_path self._find_default_library() try: return ctypes.CDLL(lib_path) except Exception as e: raise RuntimeError(fFailed to load library: {e}) def _setup_function_prototypes(self): 设置所有C函数的原型 # 对象生命周期 self._lib.DataEngine_new.argtypes [ctypes.c_int] self._lib.DataEngine_new.restype ctypes.c_void_p self._lib.DataEngine_delete.argtypes [ctypes.c_void_p] # 配置 self._lib.DataEngine_configure.argtypes [ ctypes.c_void_p, ctypes.c_char_p ] # 数据处理 self._lib.DataEngine_process.argtypes [ ctypes.c_void_p, ctypes.POINTER(DataPacket), ctypes.POINTER(DataPacket) ] # 回调 self._callback_type ctypes.CFUNCTYPE(None, ctypes.c_int, ctypes.c_void_p) self._lib.DataEngine_set_callback.argtypes [ ctypes.c_void_p, self._callback_type, ctypes.c_void_p ] def configure(self, config: str): 配置引擎 if not isinstance(config, bytes): config config.encode(utf-8) self._lib.DataEngine_configure(self._obj, config) def process(self, input_data: dict) - dict: 处理数据包 Args: input_data: 包含id和data字段的字典 Returns: 处理后的数据包字典 # 准备输入 input_packet DataPacket.from_python(input_data) # 准备输出 output_packet DataPacket() # 调用处理函数 self._lib.DataEngine_process( self._obj, ctypes.byref(input_packet), ctypes.byref(output_packet) ) # 转换为Python字典 result output_packet.to_python() # 清理资源 if input_packet.data: ctypes.free(input_packet.data) return result def set_callback(self, callback: Callable[[int], None]): 设置进度回调函数 Args: callback: 接受一个int参数(进度百分比)的函数 if callback is None: # 清除回调 self._lib.DataEngine_set_callback(self._obj, None, None) self._callback None self._user_data None return # 包装Python回调 def wrapped_callback(progress: int, _: ctypes.c_void_p): callback(progress) # 保持回调引用 self._callback self._callback_type(wrapped_callback) self._user_data ctypes.py_object(self) # 设置回调 self._lib.DataEngine_set_callback( self._obj, self._callback, ctypes.cast(self._user_data, ctypes.c_void_p) ) def __del__(self): 析构函数确保C对象被正确释放 if hasattr(self, _obj) and self._obj: self._lib.DataEngine_delete(self._obj) self._obj None staticmethod def _find_default_library() - str: 尝试在不同平台上查找默认库路径 if sys.platform win32: return DataEngine.dll elif sys.platform darwin: return libDataEngine.dylib else: return libDataEngine.so这个完整的封装示例展示了如何将面向对象的C库优雅地暴露给Python使用同时处理了内存管理、类型转换、回调函数等复杂情况。在实际项目中你可能还需要添加日志、更详细的错误检查和线程安全措施。