Написание функции pyo3, эквивалентной функции Python, которая возвращает свой входной объект - PullRequest
0 голосов
/ 27 сентября 2018

Я хочу написать Rust backend для моей библиотеки, и мне нужно реализовать эквивалент следующей функции в 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 и использовать аннотации времени жизни, чтобы компилятор был доволен, но у меня сложилось впечатление, что pyo3 намеревается управлять подсчетом ссылок сама используя время жизни объекта.

Как правильно написать эту функцию?Должен ли я пытаться обернуть его в Py контейнер?

Ответы [ 2 ]

0 голосов
/ 27 сентября 2018

Согласно другому ответу , pyo3 заботится о создании дополнительного шаблона вокруг наших функций, чтобы отслеживать подсчет ссылок Python.В частности, счетчик уже увеличивается при передаче объекта в качестве аргумента функции.Тем не менее, метод clone_ref можно использовать для явного создания новой ссылки на тот же объект, который также увеличивает его счетчик ссылок.

Выходные данные функции все еще должны бытьфактический объект Python, а не ссылка на него (что кажется разумным, поскольку Python не понимает ссылки на Rust; pyo3, похоже, игнорирует параметры времени жизни в этих функциях).

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

От игры с функциейв Python Land (AKA не серьезный испытательный стенд), это по крайней мере , кажется, работает как задумано.

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)
0 голосов
/ 27 сентября 2018

A PyObject - это простая оболочка вокруг необработанного указателя :

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

Она имеет несколько функций создания, каждая из которых соответствует различным видам указателей, которые мы можем получить из Python,Некоторые из них, такие как from_borrowed_ptr, вызывают Py_INCREF для переданного указателя.

Таким образом, кажется, что мы можем принять PyObject, пока онобыл создан "правильным" способом.

Если мы развернем этот код:

#[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.Это реализовано для PyObject путем вызова from_borrowed_ptr.

Таким образом, использование пустого PyObject в качестве типа аргумента будет правильно увеличивать счетчик ссылок.

Аналогично, когда PyObject отбрасывается в Rust, оно автоматически уменьшает счетчик ссылок .Когда он возвращается обратно в Python, право собственности передается , и это зависит от кода Python, чтобы соответствующим образом обновлять счетчик ссылок.


Все исследования выполнены для commit ed273982 из основной ветки, что соответствует v0.5.0-alpha.1.

...