PyO3 将 rust 结构转换为 &PyAny

PyO3 convert rust struct to &PyAny

我有一个结构

#[pyclass]
pub struct DynMat {
   ...
}

我有这个功能

#[pyfunction]
#[text_signature = "(tensor/)"]
pub fn exp<'py>(py: Python<'py>, tensor_or_scalar: &'py PyAny) -> PyResult<&'py PyAny> {
    // I need to return &PyAny because I might either return PyFloat or DynMat
    if let Ok(scalar) = tensor_or_scalar.cast_as::<PyFloat>() {
        let scalar: &PyAny = PyFloat::new(py, scalar.extract::<f64>()?.exp());
        Ok(scalar)
    } else if let Ok(tensor) = tensor_or_scalar.cast_as::<PyCell<DynMat>>() {
        let mut tensor:PyRef<DynMat> = tensor.try_borrow()?;
        let tensor:DynMat = tensor.exp()?;
        // what now? How to return tensor
    }
}

问题是,我怎样才能从期望 PyResult<&'py PyAny>

的函数中 return 标记为 pyclass 的 Rust 结构

我想这是你想要 return 的 tensor

如果你的 return 类型是 PyResult<DynMat> 你可以 return 然后让自动转换开始。但我假设取决于你是否有标量或张量return 不同类型。

所以,现在您有 tensor 作为 DynMat 拥有的价值 中,我们需要将其移动到 python 堆。这是它的样子:

let tensor_as_py = Py::new(py, tensor)?.into_ref(py);
return Ok(tensor_as_py);

PS:你也可以把你的转换尝试写的更简洁:

pub fn blablabla() {
  let tensor: PyRefMut<DynMat> = tensor_or_scalar.extract();
  if let Ok(tensor) = tensor {
    let tensor = tensor.exp();

但是看看你的代码,还有一件事让我感到困惑:

为了对张量取幂,您需要可变地借用它。在我看来,这表明求幂将 到位 。那你为什么还需要 return 它呢?

或者这是对原始张量的引用?在那种情况下,我会摆脱变量阴影,这样你就可以 return PyRefMut<DynMat>,你可以通过 from 或 [= 将其转换为 &PyAny 19=].

但实际上,tensor.exp()? 似乎是 return 类型 DynMat 的自有值,所以看起来 new 张量是毕竟创造了。在那种情况下,是的,您需要使用上面显示的 Py::new 方法将它从 Rust 移动到 python 堆。

编辑: 以前的版本使用 as_ref(py) 而不是 into_ref(py)。前者借用Py<_>对象给大家参考,后者实际上消耗Py<_>对象。

文档实际上在此处准确解释了您的用例 https://docs.rs/pyo3/0.13.2/pyo3/prelude/struct.Py.html#method.into_ref