如何在Rust中使用PyO3从内部修改自定义rust对象的Python列表?

huangapple go评论75阅读模式
英文:

How to modify Python list of custom rust objects from within Rust with PyO3?

问题

抱歉,您的代码示例中包含HTML转义字符,我将为您提供未经转义的代码,以便更好地理解问题:

use pyo3::prelude::*;

#[pyclass]
pub struct ListElement {
    pub value_sum: f32
}

#[pymethods]
impl ListElement {
    #[new]
    fn new(value_sum: f32) -> Self {
        ListElement { value_sum }
    }
    
    fn add_value(&mut self, value: f32) {
        self.value_sum += value;
    }
}

#[pyfunction]
fn modify_list_elements(list: Vec<&ListElement>, value: f32){
    for mut elem in list {
        elem.add_value(value);
    }
}

#[pymodule]
fn my_rust_module(_py: Python, m: &PyModule) -> PyResult<()> {
    m.add_class::<ListElement>()?;
    m.add_function(wrap_pyfunction!(modify_list_elements, m)?)?;
    Ok(())
}

Cargo.toml 文件也应该是未经HTML转义的。至于您遇到的错误,请确保您的 modify_list_elements 函数接受 Vec<&ListElement>,而不是 Vec<&amp;ListElement>。将代码中的 &lt;&amp; 更改为正常的 <&,并重新构建项目,应该能够解决这个问题。

希望这可以帮助您解决问题!如果您有其他问题,请随时提问。

英文:

I'm new to Rust and I have a problem. I have list in Python. It is a list of objects implemented in PyO3 Rust. I want to pass it to a function to make some changes to the elements.

This is minimal reproducible example:

use pyo3::prelude::*;

#[pyclass]
pub struct ListElement {
    pub value_sum: f32
}

#[pymethods]
impl ListElement {
    #[new]
    fn new(value_sum: f32) -&gt; Self {
        ListElement { value_sum }
    }
	
	fn add_value(&amp;mut self, value: f32) {
        self.value_sum += value;
    }
	
}

#[pyfunction]
fn modify_list_elements(list: Vec&lt;&amp;ListElement&gt;, value: f32){
	for mut elem in list {
          elem.add_value(value);
    }
}

#[pymodule]
fn my_rust_module(_py: Python, m: &amp;PyModule) -&gt; PyResult&lt;()&gt; {
    m.add_class::&lt;ListElement&gt;()?;
	m.add_function(wrap_pyfunction!(modify_list_elements, m)?)?;
    Ok(())
}

this is my Cargo.toml

[package]
name = &quot;my_rust_module&quot;
version = &quot;0.1.0&quot;
edition = &quot;2018&quot;

[lib]
# The name of the native library. This is the name which will be used in Python to import the
# library (i.e. `import string_sum`). If you change this, you must also change the name of the
# `#[pymodule]` in `src/lib.rs`.
name = &quot;my_rust_module&quot;
# &quot;cdylib&quot; is necessary to produce a shared library for Python to import from.
#
# Downstream Rust code (including code in `bin/`, `examples/`, and `tests/`) will not be able
# to `use string_sum;` unless the &quot;rlib&quot; or &quot;lib&quot; crate type is also included, e.g.:
# crate-type = [&quot;cdylib&quot;, &quot;rlib&quot;]
crate-type = [&quot;cdylib&quot;]

[dependencies]
pyo3 = { version = &quot;0.19.0&quot;, features = [&quot;extension-module&quot;] }

This is error that I get when I use

maturin develop
&#128279; Found pyo3 bindings
&#128013; Found CPython 3.7 at C:\my_paths\python.exe
&#128225; Using build options features from pyproject.toml
   Compiling my_rust_module v0.1.0 (C:\my_paths\my_rust_module)
error[E0277]: the trait bound `Vec&lt;&amp;ListElement&gt;: FromPyObject&lt;&#39;_&gt;` is not satisfied
  --&gt; src\lib.rs:22:31
   |
22 | fn modify_list_elements(list: Vec&lt;&amp;ListElement&gt;, value: f32){
   |                               ^^^ the trait `FromPyObject&lt;&#39;_&gt;` is not implemented for `Vec&lt;&amp;ListElement&gt;`
   |
   = help: the trait `FromPyObject&lt;&#39;a&gt;` is implemented for `Vec&lt;T&gt;`
   = note: required for `Vec&lt;&amp;ListElement&gt;` to implement `PyFunctionArgument&lt;&#39;_, &#39;_&gt;`
note: required by a bound in `extract_argument`
  --&gt; C:\my_paths\.cargo\registry\src\index.crates.io-6f17d22bba15001f\pyo3-0.19.0\src\impl_\extract_argument.rs:86:8
   |
86 |     T: PyFunctionArgument&lt;&#39;a, &#39;py&gt;,
   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `extract_argument`

For more information about this error, try `rustc --explain E0277`.

I tried to google it, understand output of rustc --explain E0277, but I still dont know how to do it.
Any help would be appreciated.

答案1

得分: 1

问题在于 Vec&lt;T&gt; 只有在 T 也实现了 FromPyObject 时才能实现,但 &amp;ListElement 并不实现该特性。

你可以通过以下两种方法之一来修复它:

  1. 派生它:

    #[pyclass]
    #[derive(FromPyObject)]
    pub struct ListElement {
        pub value_sum: f32
    }
    
  2. 改用 Vec&lt;PyRefMut&lt;ListElement&gt;&gt;,它会自动实现 FromPyObject 特性

    #[pyfunction]
    fn modify_list_elements(list: Vec&lt;PyRefMut&lt;ListElement&gt;&gt;, value: f32){
        for mut elem in list {
              elem.add_value(value);
        }
    }
    
英文:

The problem here is that Vec&lt;T&gt; only implements FromPyObject if T also does so, but &amp;ListElement doesn't implement that trait.

You can fix it in one of two ways:

  1. Derive it:
    #[pyclass]
    #[derive(FromPyObject)]
    pub struct ListElement {
        pub value_sum: f32
    }
    
  2. Take Vec&lt;PyRefMut&lt;ListElement&gt;&gt; instead for which FromPyObject is automatically implemented by the pyclass attribute:
    #[pyfunction]
    fn modify_list_elements(list: Vec&lt;PyRefMut&lt;ListElement&gt;&gt;, value: f32){
        for mut elem in list {
              elem.add_value(value);
        }
    }
    

huangapple
  • 本文由 发表于 2023年7月11日 06:07:37
  • 转载请务必保留本文链接:https://go.coder-hub.com/76657631.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定