You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何解决PyO3实现的Rust-Python模块中列表追加无效问题?

PyO3中Rust Vec属性在Python调用append后无变化的解决方法

我正在使用PyO3 v0.12.3绑定开发基于Rust的计算密集型Python模块,但Rust结构体中作为属性的空Vec列表,在Python中调用append方法后始终无法添加元素。最小复现示例如下:

Rust代码(lib.rs):

use pyo3::prelude::*;
use std::vec::Vec;

#[pyclass(subclass)]
pub struct TestClass {
    #[pyo3(get)]
    pub id: i32,
    #[pyo3(get, set)]
    pub test_list: Vec<f32>
}

#[pymethods]
impl TestClass {
    #[new]
    pub fn new(id: i32) -> TestClass {
        TestClass{
            id,
            test_list: Vec::new()
        }
    }
}

#[pymodule]
fn test_rust(_py: Python, m: &PyModule) -> PyResult<()> {
    m.add_class::<TestClass>()?;
    Ok(())
}

Python测试代码:

from test_rust import TestClass

foo = TestClass(1)

print(foo.test_list) # 输出: []
foo.test_list.append(2.3)
print(foo.test_list) # 输出: [] - 预期: [2.3]

问题原因

问题出在PyO3对Vec<T>的默认转换逻辑上:当你用#[pyo3(get, set)]修饰Vec<T>属性时,PyO3的默认getter会复制整个Rust Vec,将其转换为一个新的Python list对象返回。你调用append操作的是这个临时生成的list副本,修改后并没有将新的list写回原Rust结构体的test_list属性,因此原Vec完全没有变化。

解决方案

方案1:将Rust结构体中的Vec替换为PyList

直接使用PyO3的Py<PyList>类型存储列表,这样Python端操作的就是同一个列表对象,所有修改都会直接生效:

修改后的Rust代码:

use pyo3::prelude::*;
use pyo3::types::PyList;

#[pyclass(subclass)]
pub struct TestClass {
    #[pyo3(get)]
    pub id: i32,
    #[pyo3(get)]
    pub test_list: Py<PyList>,
}

#[pymethods]
impl TestClass {
    #[new]
    pub fn new(py: Python, id: i32) -> PyResult<Self> {
        Ok(TestClass{
            id,
            test_list: PyList::empty(py).into(),
        })
    }
}

#[pymodule]
fn test_rust(_py: Python, m: &PyModule) -> PyResult<()> {
    m.add_class::<TestClass>()?;
    Ok(())
}

此时运行原Python测试代码,append操作会直接修改test_list对应的PyList,输出会符合预期。

方案2:保留Vec,添加自定义操作方法

如果需要保留Rust端的Vec<T>(比如要利用Rust的性能优势做密集计算),可以给TestClass添加专门的方法来操作内部Vec,避免依赖自动转换的副本:

修改后的Rust代码:

use pyo3::prelude::*;
use std::vec::Vec;

#[pyclass(subclass)]
pub struct TestClass {
    #[pyo3(get)]
    pub id: i32,
    test_list: Vec<f32>, // 去掉自动get/set,改为内部私有属性
}

#[pymethods]
impl TestClass {
    #[new]
    pub fn new(id: i32) -> TestClass {
        TestClass{
            id,
            test_list: Vec::new()
        }
    }

    // 自定义append方法,直接操作内部Vec
    pub fn append(&mut self, value: f32) {
        self.test_list.push(value);
    }

    // 自定义getter,返回转换后的Python list(每次调用会生成副本,但内容是最新的)
    #[getter]
    pub fn test_list(&self, py: Python) -> PyResult<PyList> {
        let list = PyList::empty(py);
        for &val in &self.test_list {
            list.append(val)?;
        }
        Ok(list)
    }

    // 可选:添加setter,支持从Python设置整个列表
    #[setter]
    pub fn test_list(&mut self, py: Python, list: &PyList) -> PyResult<()> {
        self.test_list.clear();
        for item in list.iter() {
            let val: f32 = item.extract()?;
            self.test_list.push(val);
        }
        Ok(())
    }
}

#[pymodule]
fn test_rust(_py: Python, m: &PyModule) -> PyResult<()> {
    m.add_class::<TestClass>()?;
    Ok(())
}

对应的Python调用方式改为:

from test_rust import TestClass

foo = TestClass(1)

print(foo.test_list) # 输出: []
foo.append(2.3) # 调用自定义的append方法
print(foo.test_list) # 输出: [2.3]

内容的提问来源于stack exchange,提问作者westr

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.07 05:16:38