在Rust的PyO3中如何让自定义Python类结构体正确检查实例类型?
问题描述
我正在为一个重度依赖MessagePack的老旧Python项目开发自定义MessagePack序列化器。受GIL限制,计划用Rust实现批量序列化以提升性能,目前已支持基础Python类型,但不想移植项目中的自定义Python类到Rust,打算保留标准Python MessagePack库作为降级方案。
我编写了代表单例类FinalRule的结构体PyFinalRule,期望通过PyFinalRule::is_type_of检查PyAny对象是否为FinalRule实例,但该方法对FinalRule实例始终返回false,仅在传入其类型对象时返回true,需要解决实例类型的正确检查问题。
代码示例
struct PyFinalRule(PyAny); unsafe impl PyTypeInfo for PyFinalRule { const NAME: &'static str = "FinalRule"; const MODULE: Option<&'static str> = Option::Some("LiSE.util"); type AsRefTarget = PyAny; fn type_object_raw(py: Python<'_>) -> *mut PyTypeObject { let modu = py.import("LiSE.util").unwrap(); let final_rule = modu.getattr("FinalRule").unwrap(); final_rule.get_type_ptr() } } unsafe impl PyNativeType for PyFinalRule {}
解决方案
问题根源在于默认的is_type_of实现会直接对比对象的类型指针与type_object_raw返回的指针,而FinalRule实例的类型指针指向的是FinalRule类本身,只有当传入对象是类本身时才会匹配,实例自然返回false。
需要手动实现PyTypeCheck trait,重写is_type_of方法,调用Python原生的实例检查逻辑来判断对象是否为FinalRule实例:
修改后的完整代码
use pyo3::prelude::*; use pyo3::class::PyTypeCheck; struct PyFinalRule(PyAny); unsafe impl PyTypeInfo for PyFinalRule { const NAME: &'static str = "FinalRule"; const MODULE: Option<&'static str> = Some("LiSE.util"); type AsRefTarget = PyAny; fn type_object_raw(py: Python<'_>) -> *mut pyo3::ffi::PyTypeObject { let modu = py.import("LiSE.util").unwrap(); let final_rule = modu.getattr("FinalRule").unwrap(); final_rule.get_type_ptr() } } impl PyTypeCheck for PyFinalRule { fn is_type_of(object: &PyAny) -> bool { let py = object.py(); // 优雅处理导入和属性获取的错误,避免unwrap panic match py.import("LiSE.util") { Ok(modu) => match modu.getattr("FinalRule") { Ok(fr_type) => object.is_instance(fr_type).unwrap_or(false), Err(_) => false, }, Err(_) => false, } } }
关键说明
- 移除
PyNativeType实现:该trait是为Rust实现的Python原生类型设计的,对于包装Python侧类的场景不需要,会干扰默认类型检查逻辑。 - 重写
is_type_of:通过object.is_instance(fr_type)调用Python原生的isinstance逻辑,这是检查Python对象是否属于某个类实例的标准方式,能正确识别FinalRule的实例。
内容的提问来源于stack exchange,提问作者logicaldash
相关产品推荐
相关产品推荐

