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

无需复制数据将std::vector转为boost::python::list(只读访问)

这个需求太常见了——想把C++里的std::vector暴露给Python只读访问,又不想复制底层数据,毕竟写个全转发的包装类实在繁琐。我给你分享两个更优雅的方案,看你的场景选哪个:

方案1:自定义只读序列包装器(通用型)

这个方法适用于任何类型的std::vector,不管是基础类型还是自定义类。核心思路是写一个轻量的C++包装类,只实现Python序列的只读接口(__len__和__getitem__),直接指向原std::vector的内存,完全不复制数据。

C++端实现

#include <boost/python.hpp>
#include <vector>

// 通用的只读vector包装器
template<typename T>
class VectorReadOnlyWrapper {
public:
    explicit VectorReadOnlyWrapper(const std::vector<T>& vec) : m_vec(&vec) {}

    // 对应Python的len()
    size_t __len__() const {
        return m_vec->size();
    }

    // 对应Python的[]索引访问
    const T& __getitem__(size_t index) const {
        if (index >= m_vec->size()) {
            PyErr_SetString(PyExc_IndexError, "Index out of range");
            boost::python::throw_error_already_set();
        }
        return (*m_vec)[index];
    }

private:
    // 保存原vector的const指针,确保只读
    const std::vector<T>* m_vec;
};

// 模板函数,快速暴露不同类型的包装器
template<typename T>
void expose_vector_readonly(const char* wrapper_name) {
    using namespace boost::python;
    class_<VectorReadOnlyWrapper<T>>(wrapper_name, init<const std::vector<T>&>())
        .def("__len__", &VectorReadOnlyWrapper<T>::__len__)
        .def("__getitem__", &VectorReadOnlyWrapper<T>::__getitem__);
}

// 模块示例
BOOST_PYTHON_MODULE(my_module) {
    // 暴露int类型的只读vector包装器
    expose_vector_readonly<int>("IntVectorReadOnly");

    // 假设你有一个返回vector<int>的函数(注意用reference_existing_object确保不复制)
    std::vector<int>& get_my_int_vector() {
        static std::vector<int> data = {10, 20, 30, 40, 50};
        return data;
    }
    def("get_my_int_vector", &get_my_int_vector, 
        return_value_policy<reference_existing_object>());
}

Python端使用

import my_module

# 获取原vector的引用(无复制)
raw_vec = my_module.get_my_int_vector()
# 创建只读包装器
read_only_vec = my_module.IntVectorReadOnly(raw_vec)

# 像操作列表一样访问
print(len(read_only_vec))  # 输出5
print(read_only_vec[2])    # 输出30

# 尝试修改会报错(因为包装器没实现__setitem__)
# read_only_vec[2] = 100  # 抛出AttributeError
方案2:用numpy直接映射内存(数值类型最优)

如果你的std::vector存的是数值类型(int、float、double等),那用numpy是最爽的选择——不仅完全零复制,还能直接利用numpy的所有数组操作,而且Python生态里大家都熟悉numpy数组。

C++端实现

只需要暴露原vector的数据指针和大小即可:

#include <boost/python.hpp>
#include <vector>

// 示例:返回float类型的vector
std::vector<float>& get_my_float_vector() {
    static std::vector<float> data = {1.5, 2.7, 3.9, 4.2};
    return data;
}

BOOST_PYTHON_MODULE(my_module) {
    using namespace boost::python;

    // 暴露获取数据指针的函数
    def("get_float_data", +[](std::vector<float>& vec) { return vec.data(); },
        return_value_policy<reference_existing_object>());
    
    // 暴露获取大小的函数
    def("get_float_size", +[](std::vector<float>& vec) { return vec.size(); });
    
    // 暴露原vector的引用
    def("get_my_float_vector", &get_my_float_vector,
        return_value_policy<reference_existing_object>());
}

Python端使用

用numpy.frombuffer直接映射内存,然后设置为只读:

import my_module
import numpy as np

vec = my_module.get_my_float_vector()
data_ptr = my_module.get_float_data(vec)
size = my_module.get_float_size(vec)

# 创建numpy数组,直接映射C++内存
np_arr = np.frombuffer(data_ptr, dtype=np.float32, count=size)
# 设置为只读,防止误修改
np_arr.flags.writeable = False

print(np_arr)  # 输出[1.5 2.7 3.9 4.2]
# 尝试修改会报错
# np_arr[0] = 0.0  # 抛出ValueError
总结
  • 如果是自定义类型的vector,选方案1的只读序列包装器,通用且灵活;
  • 如果是数值类型的vector,选方案2的numpy映射,效率最高还能兼容Python数据生态;
  • 两种方案都完全避免了底层数据的复制,比写全属性转发的包装类简洁太多。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 06:25:30