1

我有以下函数,它使用 PyO3 调用 python 函数并获得结果(在这种情况下,int分配给 a i32):

fn run_python<'a, T: FromPyObject<'a> + Clone>(func_name: &str) -> Result<T, ()> {
    Python::with_gil(|py| {
        let pyapi = match py.import("pyapi") {
            Ok(v) => v,
            Err(e) => { e.print_and_set_sys_last_vars(py); return Err(()) },
        };

        let locals = [("pyapi", pyapi)].into_py_dict(py);
        let eval_result: PyResult<&PyAny> = py.eval("pyapi.{}(**kwargs)", None, Some(&locals));

        let wrapped_obj: &PyAny = match eval_result {
            Ok(v) => v,
            Err(e) => { e.print_and_set_sys_last_vars(py); return Err(()) },
        };

        let unwrapped_result: PyResult<T> = wrapped_obj.extract();

        match unwrapped_result {
            Ok(v) => return Ok(v.clone()),
            Err(e) => return Err(()),
        };
    })
}

当我尝试编译时,出现以下错误:

error[E0495]: cannot infer an appropriate lifetime for lifetime parameter `'p` due to conflicting requirements
   --> src\bin\launch.rs:89:30
    |
89  |         let eval_result = py.eval("pyapi.{}(**kwargs)", None, Some(&locals));
    |                              ^^^^
    |
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the body at 82:22...
   --> src\bin\launch.rs:82:22
    |
82  |       Python::with_gil(|py| {
    |  ______________________^
83  | |         let pyapi = match py.import("pyapi") {
84  | |             Ok(v) => v,
85  | |             Err(e) => { e.print_and_set_sys_last_vars(py); return Err(()) },
...   |
101 | |         };
102 | |     })
    | |_____^
note: ...so that the types are compatible
   --> src\bin\launch.rs:89:30
    |
89  |         let eval_result = py.eval("pyapi.{}(**kwargs)", None, Some(&locals));
    |                              ^^^^
    = note: expected `pyo3::Python<'_>`
               found `pyo3::Python<'_>`
note: but, the lifetime must be valid for the lifetime `'a` as defined on the function body at 81:15...
   --> src\bin\launch.rs:81:15
    |
81  | fn run_python<'a, T: FromPyObject<'a> + Clone>(func_name: &str) -> Result<T, ()> {
    |               ^^
note: ...so that the types are compatible
   --> src\bin\launch.rs:96:57
    |
96  |         let unwrapped_result: PyResult<T> = wrapped_obj.extract();
    |                                                         ^^^^^^^
    = note: expected `pyo3::FromPyObject<'_>`
               found `pyo3::FromPyObject<'a>`

我对 Rust 很陌生,可能正在做一些愚蠢的事情(很可能是 X/Y 问题)。我怎样才能得到一个py.eval与python解释器没有生命周期的价值(这是我假设在这里发生的事情)?

4

1 回答 1

1

Ry 建议的修复方法似乎效果很好。Usingfor<'p>让编译器将生命周期的评估推迟到处理调用.extract(). 并且生命周期'p不需要在函数的通用参数列表中指定。此外,Clone绑定不是必需的。

fn run_python<T>(func_name: &str) -> Result<T, ()>
where
    T: for<'p> FromPyObject<'p>,
{
    let guard = Python::acquire_gil();
    let py    = guard.python();    
    match || -> _ { // try...
        let pyapi  = py.import("pyapi")?; // throw...
        let locals = [("pyapi", pyapi)].into_py_dict(py);            
        py.eval(&format!("pyapi.{}(**kwargs)", func_name), 
                None, 
                Some(&locals))?.extract()
    }() { // catch...
        Err(e) => {
            // Error handling specific to Pyo3.
            e.print_and_set_sys_last_vars(py);
            // Return the error type spec'd in the fn signature.
            Err(())
        },
        Ok(obj) => Ok(obj),
    }    
}

这种方法的唯一限制是.extract()转换为的类型不需要依赖于它转换的类型的生命周期。例如,如果run_python()可以返回字符串列表,这是可能的:

let values: Vec<String> = run_python("make_string_list").unwrap(); // OK.

但这会产生与生命周期相关的编译器错误,尽管.extract()能够在正确的条件下产生这种类型:

let values: Vec<&str> = run_python("make_string_list").unwrap(); // Error.

如果run_python()需要能够产生依赖于生命周期的值,那么一个解决方案可能是调用者获取 GIL,并传入一个Python实例。该函数可能如下所示:

fn run_python<'p, T>(func_name: &str, py: Python<'p>) -> PyResult<T>
where
    T: FromPyObject<'p>,
{
    let pyapi  = py.import("pyapi")?;
    let locals = [("pyapi", pyapi)].into_py_dict(py);            
    py.eval(&format!("pyapi.{}(**kwargs)", func_name), 
            None, 
            Some(&locals))?.extract()
}

在我意识到使用建议for<>是最好的选择之前,我开始写另一个答案。我曾假设返回值对 GIL 有一定的依赖性,但.extract()返回的类型不依赖于 GIL。

在上一个答案中,我建议了处理需要在 GIL 生命周期之外保存的 Python 对象的方法。这涉及使用 将 GIL-Dependent 类型转换为 GIL-Independent 类型.to_object(py),然后在需要时使用方法再次返回.cast_as::<PyType>(py)

于 2021-08-08T02:05:19.413 回答