PyO3将Rust结构体转换为&PyAny。

3

我有一个结构体

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

and I have this function

#[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>的函数中返回一个标记有pyclass的Rust结构体?
1个回答

2
我认为您想要返回的是“张量”。 如果您的返回类型是“PyResult < DynMat>”,则可以只需返回该类型并让自动转换生效。但我认为,根据您是返回标量还是张量,您将返回不同的类型。 因此,目前您拥有一个作为“DynMat”的“tensor”,我们需要将其移动到Python堆中。以下是如何实现的:
let tensor_as_py = Py::new(py, tensor)?.into_ref(py);
return Ok(tensor_as_py);

PS:您也可以更简洁地写下您的转换尝试:


注意:本文中的“conversion”应该翻译为“转换尝试”,而非“转化”。
pub fn blablabla() {
  let tensor: PyRefMut<DynMat> = tensor_or_scalar.extract();
  if let Ok(tensor) = tensor {
    let tensor = tensor.exp();

但是看着你的代码,还有一件事情让我感到困惑:
为了对张量进行指数运算,你正在借用它的可变引用。这让我觉得指数运算将会在原地进行。那么你为什么还需要返回它呢?
或者这是要引用回原始张量吗?如果是这样,我会消除变量遮蔽,这样你就可以直接返回 PyRefMut<DynMat>,然后通过 frominto 将其转换为 &PyAny
但实际上,tensor.exp()? 似乎返回了一个类型为 DynMat 的所有权值,所以看起来确实会创建一个新的张量。在这种情况下,是的,你需要使用上面显示的 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

1
我在使用借用检查器时遇到了问题。如果我按照您所说的采用引用方式,如 Py::new(py, tensor)?.as_ref(py),我会得到“temporary value created here”和“returns a value referencing data owned by the current function”的警告信息。我该怎么确保创建的引用使用 'py 的生命周期呢? - alagris
1
啊,我其实还没有尝试编译,而是以为IDE会告诉我这个问题:D只是一个简单的拼写错误,请看我的编辑。 - cadolphs

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接