作为一个学习练习,我尝试使用闭包在pyo3中实现一个参数化的装饰器函数。(非参数化的)装饰器被实现为具有__call__
方法的类,而我我在此基础上构建并创建了一个参数化装饰器,它使用一个外部类和一个__call__
方法,该方法返回一个内部类和一个__call__
,该内部类调用目标函数,并且它工作了。但是作为一个学习练习(我需要特别提高我对生命周期的理解),我想尝试在闭包方面实现同样的事情。(注意我以前在C++中用lambdas做过)
因此,我的非参数化装饰器,经过一些试验和与编译器的斗争,看起来像这样:
#[pyfunction]
pub fn exectime(py: Python, wraps: PyObject) -> PyResult<&PyCFunction> {
PyCFunction::new_closure(
py, None, None,
move |args: &PyTuple, kwargs: Option<&PyDict>| -> PyResult<PyObject> {
Python::with_gil(|py| {
let now = Instant::now();
let ret = wraps.call(py, args, kwargs);
println!("elapsed (ms): {}", now.elapsed().as_millis());
ret
})
}
)
}
注意我需要将捕获的py
Package 在Python::with_gil
中才能使其工作。尝试将其扩展到我提出的嵌套装饰器:
#[pyfunction]
pub fn average_exectime(py: Python, n: usize) -> PyResult<&PyCFunction> {
let f = move |args: &PyTuple, _kwargs: Option<&PyDict>| -> PyResult<&PyCFunction> {
Python::with_gil(|py| {
let wraps: PyObject = args.get_item(0)?.into();
let g = move |args: &PyTuple, kwargs: Option<&PyDict>| -> PyResult<PyObject> {
Python::with_gil(|py| {
let now = Instant::now();
for _ in 0..n-1 {
wraps.call(py, args, kwargs);
}
let ret = wraps.call(py, args, kwargs);
println!("elapsed (ms): {}", now.elapsed().as_millis());
ret
})
};
PyCFunction::new_closure(py, None, None, g)
})
};
PyCFunction::new_closure(py, None, None, f)
}
编译器告诉我:
error: lifetime may not live long enough ] 44/45: poetry-rust-integration
--> src/decorator.rs:48:13
|
35 | Python::with_gil(|py| {
| --- return type of closure is Result<&'2 pyo3::types::PyCFunction, pyo3::PyErr>
| |
| has type `pyo3::Python<'1>`
...
48 | PyCFunction::new_closure(py, None, None, g)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ returning this value requires that `'1` must outlive `'2`
Building [=========================> ] 44/45: poetry-rust-integration error: aborting due to previous error
我尝试了各种各样的生存期参数,包括封闭的生存期都没有用,我只是得到了更多的错误。我想我不明白为什么编译器认为内部的生存期必须比另一个长?告诉编译器它们有相同的生存期还不够吗?如果是这样,如何实现?
2条答案
按热度按时间uurity8g1#
因此,您得到的
&PyCFunction
仅对with_gil
块的范围有效。要使函数比GIL块更有效,需要将其转换为a GIL-independent reference。
ctrmrzij2#
非常感谢Masklinn解释了这个错误并为我指出了正确的方向。我现在有了这个可行的解决方案: