从boost::python::object列表中获取单个元素以在Python例程中使用

2 投票
2 回答
4542 浏览
提问于 2025-04-18 00:04

我有一个C++的Boost Python对象,这个对象是一个Python列表的封装。

  PyObject * pyList = func(...);
  boost::python::object listObj(handle<>(boost::python::borrowed(pyList)));

我可以通过一些操作来确认它确实是一个列表,比如说:

boost::python::object np = import("numpy");
boost::python::np_difference = np.attr("diff");
np_difference(listObj);
for(int i=0; i<len(listObj); i++){                                                                                         
  double value = boost::python::extract<double>(listObj[i]);                                                                              
  cout << i << " " << value << endl;                                                                                       
} 

这个操作会把第i个元素减去第i-1个元素,然后创建一个新的列表,接着把每个元素提取出来并在C++中打印到标准输出。

我想做的是用这个列表对象来调用我定义的打印函数:

boost::python::object print = std.attr("print");    

但我只想打印我指定的某个元素。在Python中,我只需要写:

print myList[0]

因为它就是一个普通的Python列表。但是当我在C++中用Boost Python尝试:

print(listObj[0]);

时,我得到了:

Error in Python: <type 'exceptions.TypeError'>: No to_python (by-value) converter found for C++ type: boost::python::api::proxy<boost::python::api::item_policies>.

那么,我该如何从Python列表对象中访问单个元素,并在C++中像上面那样调用Python的打印方法(或者任何其他需要字符串作为输入的Python函数)呢?

2 个回答

0

编辑

Tanner Sansbury 的回答是用 C++ 的方式来解决这个问题,而我下面介绍的方法只是一个变通办法,它可以让你把一个 Python 对象放到 boost::python 命名空间中,然后再赋值到 __main__ 的 Python 解释器命名空间里。


明白了。从这里得到的启发,我们需要把 boost::python::object 赋值到 Python 的命名空间,也就是:

    boost::python::object main_module = boost::python::import("__main__");
    boost::python::object main_namespace = main_module.attr("__dict__");
    main_module.attr("myList") = listObj;  
    boost::python::exec("print myList", main_namespace);  

希望这对你有帮助。

5

boost::python::objectoperator[] 方法会返回一个 boost::python::proxy 对象。虽然 proxy 类可以隐式转换为 boost::python::object,但在很多地方的API中,还是需要明确的转换。

如果你想从 proxy 明确构造一个 boost::python::object,这样可以解决转换异常:

print(boost::python::object(listObjString[0]));

下面是一个完整的嵌入式Python示例,演示如何通过以下方式打印列表中的单个元素:

  • 使用Python内置的 print 函数
  • 使用Python/C API中的 PyObject_Print() 函数
  • 通过 __str__ 提取对象的字符串表示,并用 std::cout 打印
#include <boost/foreach.hpp>
#include <boost/python.hpp>
#include <boost/range/irange.hpp>

/// @brief Default flag to have PyObject_Print use the object's
///        __str__ method.  The python.h files only define the flag for
///        __repr__.
#define Py_PRINT_STR 0

int main()
{
  Py_Initialize();

  namespace python = boost::python;
  try
  {
    // Create and populate a Python list.
    // >>> list = [x for x in range(100, 103)]
    python::list list;    
    BOOST_FOREACH(int x, boost::irange(100, 103))
        list.append(x);

    // The proxy returned from a Boost.Python's operator[] provides a
    // user-defined conversion to a Boost.Python object.  In most cases,
    // explicitly invoking the conversion is required.

    // Print list[0] using the built-in function print.
    /// >>> getattr(__builtins__, 'print')(list[0])
    python::object print =
        python::import("__main__").attr("__builtins__").attr("print");
    print(python::object(list[0]));

    // Print list[1] using the Python/C API.
    // >>> import sys; sys.stdout.write(list[1].__str__())
    PyObject_Print(python::object(list[1]).ptr(), stdout, Py_PRINT_STR);
    std::cout << std::endl;

    // Print list[2] using the result of the object's __str__ method, and
    // extract a C++ string.
    std::cout << python::extract<std::string>(
                   python::object(list[2]).attr("__str__")())() << std::endl;

  }
  catch (python::error_already_set&)
  {
    PyErr_Print();
  }
}

输出:

100
101
102

撰写回答