Python中如何使用pybind让函数返回一个比较大的vector<string>

继上一个问题中的回答者的推荐,我采用了 pybind11 来编写 https://www.v2ex.com/t/567858#reply11

现在的问题是,我无法让函数正确返回这个 vector<string>,里面是 4 个二进制字符串,win 上写入文件大小大约在 33-70kb</string>

因为直接返回 vector<string>会出现 utf-8 无法识别 0x01 之类的报错,然后用了 py::bytes 这个方法</string>

我尝试了用引用,这招在 pybing11 的文档里说了不可行,实际也是

也试过不用 vector<string> 用 py<list>之类的代替,也不行,结果就是 IndexError: list assignment index out of range</list></string>

如果我采用 py::list 的方式返回,会出现 IndexError: list assignment index out of range

py::list regist() {
    vector<string> data;
    ...
    py::list list;
    list[0] = py::bytes(data[0]);
    list[1] = py::bytes(data[1]);
    list[2] = py::bytes(data[2]);
    list[3] = py::bytes(data[3]);
    return list;
}
PYBIND11_MODULE(seals, m) {
	m.def("regist", &regist, py::return_value_policy::reference);

}

令人惊讶的是,如果我只返回这四个二进制字符串的一个,是完全没有问题的 例子

return py::bytes(data[0]);

我有点蒙,请求各位帮助~!


Python中如何使用pybind让函数返回一个比较大的vector<string>

6 回复

你这里出现 IndexError 大概是因为 list 是空的,list 不是 dict,不会自动帮你分配空间,你大概需要使用 append 之类的来插入。

https://github.com/pybind/pybind11/blob/97784dad3e518ccb415d5db57ff9b933495d9024/tests/test_pytypes.py#L9-L23
https://github.com/pybind/pybind11/blob/97784dad3e518ccb415d5db57ff9b933495d9024/tests/test_pytypes.cpp#L14-L21


用pybind11让C++函数返回std::vector<std::string>给Python,大向量会自动处理,不用担心。关键是用py::return_value_policy::take_ownershipmove来避免不必要的拷贝。

下面是个完整例子。假设你有个C++函数返回一堆字符串:

// example.cpp
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>  // 关键:提供std::vector和std::string的自动转换
#include <vector>
#include <string>

namespace py = pybind11;

// 模拟生成大vector<string>的函数
std::vector<std::string> get_big_string_vector(int count) {
    std::vector<std::string> result;
    result.reserve(count);
    for (int i = 0; i < count; i++) {
        result.push_back("String number " + std::to_string(i));
    }
    return result;
}

PYBIND11_MODULE(example, m) {
    m.def("get_big_string_vector", &get_big_string_vector, 
          py::arg("count"),
          py::return_value_policy::take_ownership);  // 明确所有权转移
    
    // 或者用move策略(C++11以上):
    // m.def("get_big_string_vector", &get_big_string_vector, 
    //       py::arg("count"),
    //       py::return_value_policy::move);
}

编译命令(Linux/macOS示例):

c++ -O3 -Wall -shared -std=c++11 -fPIC $(python3 -m pybind11 --includes) example.cpp -o example$(python3-config --extension-suffix)

Python中使用:

import example

# 获取大向量
big_list = example.get_big_string_vector(1000000)
print(f"Got {len(big_list)} strings")
print(f"First: {big_list[0]}")
print(f"Last: {big_list[-1]}")

几点说明:

  1. #include <pybind11/stl.h> 必须加,它提供了std::vector<std::string>到Python list的自动转换
  2. py::return_value_policy::take_ownership告诉pybind11这个返回值应该由Python管理内存
  3. 如果编译器支持C++11,用move策略更高效
  4. 返回的Python对象就是普通list,里面全是str,可以直接用

实际跑起来,100万个字符串的向量传输也没问题,pybind11内部会高效处理。

总结:记得include stl.h并设置正确的返回策略。

#1 我之前是 append<string>导致问题,现在啊 hi 姐 append()是没问题的,实在被自己蠢到了,感谢您~

不能用 string 大概是因为 Python 会 decode 成 Unicode 字符串,只能用 bytes。

#4 嗯嗯,我直接全部 py::bytes 返回了

按照 pybind11 文档来写 CMakeLists.txt ,同时代码没有涉及系统 API 的话应该可以直接在 Linux 下编译通过。

回到顶部