我正在为自己的库编写Rust后端,我需要在pyo3
中实现以下功能的等效项:
def f(x):
return x
这应该返回相同的对象作为输入,并且获取返回值的函数应持有对该输入的新引用。如果我使用C API编写此代码,则将其编写为:
PyObject * f(PyObject * x) {
Py_XINCREF(x);
return x;
}
在PyO3中,浏览PyObject
,PyObjectRef
,&PyObject
,Py<PyObject>
,Py<&PyObject>
之间的差异非常令人困惑。
此功能最原始的版本是:
extern crate pyo3;
use pyo3::prelude::*;
#[pyfunction]
pub fn f(_py: Python, x: &PyObject) -> PyResult<&PyObject> {
Ok(x)
}
除其他事项外,x
的生存期和返回值不相同,再加上pyo3
我没有机会增加x
的引用计数,实际上编译器似乎同意我的观点:
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
参数来手动增加引用计数,并使用生命周期批注使编译器满意,但我的印象是{{1} }打算使用对象生存期来管理引用计数本身。
编写此函数的正确方法是什么?我是否应该尝试将其包装在pyo3
容器中?
答案 0 :(得分:5)
一个PyObject
是a simple wrapper around a raw pointer:
pub struct PyObject(*mut ffi::PyObject);
它具有多个创建函数,每个函数对应于我们可能从Python获得的不同类型的指针。其中的一些,例如from_borrowed_ptr
,会在传入的指针上调用Py_INCREF
。
因此,只要我们以“正确”的方式创建PyObject
,我们似乎就可以接受它。
如果我们expand此代码:
#[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,
))
},
)
我们的论点是通过调用ObjectProtocol::extract
创建的,该调用依次调用FromPyObject::extract
。通过调用from_borrowed_ptr
来implemented for PyObject
。
因此,使用裸露的PyObject
作为参数类型将正确地增加引用计数。
同样,将PyObject
放入Rust时,它将自动decrease the reference count。返回到Python后,ownership is transferred由Python代码决定是否适当地更新引用计数。
在master分支中为commit ed273982完成的所有调查,对应于v0.5.0-alpha.1。
答案 1 :(得分:4)
根据to the other answer,pyo3
,我们需要围绕我们的函数构建其他样板,以便跟踪Python参考计数。特别是,将对象作为参数传递给函数时,计数器已经递增。不过,clone_ref
方法可用于显式创建对同一对象的新引用,这也会增加其引用计数器。
该函数的输出仍然必须是实际的Python对象,而不是对其的引用(这似乎是合理的,因为Python无法理解Rust引用; pyo3
似乎忽略了这些函数中的生存期参数)。 / p>
#[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)