编写一个等价于 Python 函数的 pyo3 函数,returns 它的输入对象

Writing a pyo3 function equivalent to a Python function that returns its input object

我正在为我的库编写一个 Rust 后端,我需要在 pyo3 中实现以下函数的等价物:

def f(x):
    return x

这应该 return 与输入相同的对象 ,并且获取 return 值的函数应该持有对输入的新引用。如果我用 C API 写这个,我会写成:

PyObject * f(PyObject * x) {
    Py_XINCREF(x);
    return x;
}

PyO3 中,我发现很难理解 PyObjectPyObjectRef&PyObjectPy<PyObject>、[=21= 之间的差异].

这个函数最幼稚的版本是:

extern crate pyo3;

use pyo3::prelude::*;

#[pyfunction]
pub fn f(_py: Python, x: &PyObject) -> PyResult<&PyObject> {
    Ok(x)
}

除其他外,x 和 return 值的生命周期不同,而且我认为 pyo3 没有机会增加 [=22] 的引用计数=],事实上编译器似乎同意我的观点:

error[E0106]: missing lifetime specifier
 --> src/lib.rs:4:49
  |
4 | pub fn f(_py: Python, x: &PyObject) -> PyResult<&PyObject> {
  |                                                 ^ expected lifetime parameter
  |
  = help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from `_py` or `x`

我可能有办法 手动 使用 _py 参数增加引用计数并使用生命周期注释来使编译器满意,但我的印象是pyo3 打算使用对象生命周期管理引用计数 自身

写这个函数的正确方法是什么?我应该尝试将它包装在 Py 容器中吗?

A PyObjecta simple wrapper around a raw pointer:

pub struct PyObject(*mut ffi::PyObject);

它有多个创建函数,每个对应于我们可能从Python得到的不同种类的指针。其中一些,例如 from_borrowed_ptr,在传入的指针上调用 Py_INCREF

因此,似乎我们可以接受 PyObject,只要它是以 "right" 方式创建的。

如果我们这个代码:

#[pyfunction]
pub fn example(_py: Python, x: PyObject) -> PyObject {
    x
}

我们可以看到调用我们函数的这段代码:

let mut _iter = _output.iter();
::pyo3::ObjectProtocol::extract(_iter.next().unwrap().unwrap()).and_then(
    |arg1| {
        ::pyo3::ReturnTypeIntoPyResult::return_type_into_py_result(example(
            _py, arg1,
        ))
    },
)

我们的论点是通过调用 from_borrowed_ptr.

ObjectProtocol::extract, which in turn calls FromPyObject::extract. This is implemented for PyObject 的调用创建的

因此,使用裸 PyObject 作为参数类型将正确地增加引用计数。

同样,当在 Rust 中删除 PyObject 时,它会自动 decrease the reference count. When it is returned back to Python, ownership is transferred 并且由 Python 代码来适当地更新引用计数。


从 master 分支为 commit ed273982 完成的所有调查,对应于 v0.5.0-alpha.1。

根据 , pyo3 takes care of building additional boilerplate around our functions in order to keep track of Python reference counting. In particular, the counter is already incremented when passing the object as an argument to the function. Nevertheless, the clone_ref 方法可用于显式创建对同一对象的新引用,这也会增加其引用计数器。

函数的输出仍然必须是一个实际的 Python 对象而不是对它的引用(这似乎是合理的,因为 Python 不理解 Rust 引用;pyo3 似乎忽略这些函数中的生命周期参数)。

#[pyfunction]
fn f(py: Python, x: PyObject) -> PyResult<PyObject> {
    Ok(x.clone_ref(py))
}

在 Python 土地(又名不是一个严肃的测试平台)中试用该功能,它 至少 似乎按预期工作。

from dummypy import f

def get_object():
    return f("OK")

a = [1, 2, 3]

if True:
    b = f(a)
    assert b is a
    b[0] = 9001

print(a)

x = get_object()
print(x)