如何使用 PYO3 实现返回 Python 对象的 Rust 函数

Gin*_*nty 6 rust pyo3

我想从 Python 调用返回 Python 对象的 Rust 函数:

my_rust_module.my_function()  # => <object>
Run Code Online (Sandbox Code Playgroud)

我不确定如何创建这个函数,因为类实例化的 PYO3 指南描述了如何将这样的对象实例化为 a PyRef,但是我不知道如何从 a 返回这样的引用pyfunction

这就是我正在尝试的:

#[pyfunction]
fn my_function(py: Python) -> PyRef {
    let gil = Python::acquire_gil();
    let py = gil.python();
    PyRef::new(py, MyStruct { }).unwrap()
}
Run Code Online (Sandbox Code Playgroud)

但是,PyRef似乎不是有效的返回类型(编译器说“类型参数的数量错误:预期为 1,发现为 0”),并且我不知道如何将 a 转换PyRef为可以返回的内容,例如 a PyObject

edw*_*rdw 3

鉴于适当的脚手架已经到位,例如pymodulepyclass定义,有多种方法可以实现此目的:

#[pyfunction]
fn my_function() -> PyResult<PyObject> {
    let gil = Python::acquire_gil();
    let py = gil.python();
    let pyref = PyRef::new(py, MyStruct {})?;

    Ok(pyref.to_object(py))
}
Run Code Online (Sandbox Code Playgroud)

或者:

#[pyfunction]
fn my_function() -> PyResult<Py<MyStruct>> {
    let gil = Python::acquire_gil();
    let py = gil.python();

    Py::new(py, MyStruct {})
}
Run Code Online (Sandbox Code Playgroud)

我还冒昧地包装了返回的对象,PyResult以便将可能的错误传播到 python 领域。您可以用 Rust 解开包装并返回裸露的对象,但我认为建议采用这种处理错误的方式。


编辑:之前的答案并不完整,而且有点误导。最简单的方法实际上就是:

#[pyfunction]
fn my_function() -> PyResult<MyStruct> {
    Ok(MyStruct {})
}
Run Code Online (Sandbox Code Playgroud)

PYO3pyclass指南指出:

您可以像普通的 Rust 结构一样使用 pyclass。

但是,如果正常实例化,则不能将 pyclass 视为 Python 对象。

为了获得包含 pyclass 的 Python 对象,我们必须使用一些特殊的方法。

我相信只有当所述值不跨越 Rust / Python 边界时这才是正确的。如果是,pyfunction宏会自动将 Python 对象转换为 Rust 值,并将 Rust 返回值转换回 Python 对象。PYO3 指南在这里可能更具体。