如何在Rust中获取PyO3里PyAny对象的Python ID数值
如何在PyO3中获取Python对象的ID并转为Rust数值?
我用PyO3在Rust里开发Python模块,定义了一个MyClass结构体,它接收Py<PyAny>类型来引用Python对象。现在要给这个Rust类实现哈希逻辑,目的是当多个MyClass实例引用同一个Python对象时能自动去重,所以需要把Python对象的ID转成Rust能处理的数值传给哈希器。
目前的问题是:虽然在Rust里能看到Py<PyAny>对象打印出来的Python ID,但没法直接转成普通数值;尝试访问PyAny的私有字段获取ID时,Rust直接报错。
示例代码
Rust代码(初始版本)
#[pyclass] pub struct MyClass { obj: Option<Py<PyAny>>, } #[pymethods] impl MyClass { #[new] fn new(obj: Option<Py<PyAny>>) -> Self { if obj.is_some() { println!("Obj: {:?}", obj.as_ref()); } Self { obj } } }
Python测试代码
obj = [1,2,3,4] print(hex(id(obj))) # '0x103da9100' MyClass(obj) # Obj: Some(Py(0x103da9100))
错误的尝试代码(访问私有字段)
let obj_id = obj?.0;
解决方法:用PyO3官方API获取ID
PyO3提供了合法的方法获取Python对象的ID,不需要去碰私有字段。Python的id()函数本质上返回的是对象在内存中的指针地址,而PyO3的PyAny::as_ptr()方法就能拿到这个指针,把它转成usize类型就是我们需要的数值。
修改后的完整Rust代码
use pyo3::prelude::*; use std::hash::{Hash, Hasher}; #[pyclass] pub struct MyClass { obj: Option<Py<PyAny>>, } #[pymethods] impl MyClass { #[new] fn new(obj: Option<Py<PyAny>>, py: Python<'_>) -> Self { if let Some(ref py_obj) = obj { // 获取Python对象ID并转为Rust的usize数值 let obj_id = py_obj.as_ref(py).as_ptr() as usize; println!("Python对象ID(十进制): {}", obj_id); println!("Python对象ID(十六进制): {:x}", obj_id); } Self { obj } } } // 实现Hash trait,基于Python对象的ID生成哈希值 impl Hash for MyClass { fn hash<H: Hasher>(&self, state: &mut H) { Python::with_gil(|py| { match &self.obj { Some(obj) => { let obj_id = obj.as_ref(py).as_ptr() as usize; obj_id.hash(state); } None => { // 处理没有引用Python对象的情况,用固定值哈希 0.hash(state); } } }); } } // 实现PartialEq和Eq,保证哈希去重的正确性 impl PartialEq for MyClass { fn eq(&self, other: &Self) -> bool { Python::with_gil(|py| { match (&self.obj, &other.obj) { (Some(a), Some(b)) => a.as_ref(py).as_ptr() == b.as_ref(py).as_ptr(), (None, None) => true, _ => false, } }) } } impl Eq for MyClass {}
关键说明
- GIL保护:操作Python对象必须在GIL(全局解释器锁)的保护下进行,
Python::with_gil()会自动获取并释放GIL;在#[pymethods]的函数里可以直接通过参数拿到py: Python<'_>来使用。 - 指针转数值:
as_ptr()返回的*mut ffi::PyObject类型,转成usize后就和Pythonid()返回的数值完全一致。 - 哈希与相等性:要实现正确的去重,必须同时实现
Hash和PartialEq/Eqtrait,这样哈希集合(比如std::collections::HashSet)才能正确识别出引用同一个Python对象的MyClass实例。
内容的提问来源于stack exchange,提问作者David Chanin
相关产品推荐
相关产品推荐

