无需复制数据将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
相关产品推荐
相关产品推荐

