You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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 {}

关键说明

  1. GIL保护:操作Python对象必须在GIL(全局解释器锁)的保护下进行,Python::with_gil()会自动获取并释放GIL;在#[pymethods]的函数里可以直接通过参数拿到py: Python<'_>来使用。
  2. 指针转数值:as_ptr()返回的*mut ffi::PyObject类型,转成usize后就和Python id()返回的数值完全一致。
  3. 哈希与相等性:要实现正确的去重,必须同时实现Hash和PartialEq/Eq trait,这样哈希集合(比如std::collections::HashSet)才能正确识别出引用同一个Python对象的MyClass实例。

内容的提问来源于stack exchange,提问作者David Chanin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 06:01:32