【问题标题】:Python* to boost::python::objectPython* 到 boost::python::object
【发布时间】:2015-07-17 05:06:14
【问题描述】:

我正在尝试在 C++ 中构建一个 Python 模块,将 2D 向量转换为 Numpy 2D 数组。这里有什么不正确的 - 大概需要一些转换才能从 PyObject* 提升 python 对象?

boost::python::object build_day(int year, int day) {

  PyObject* arr;
  const int HEIGHT = 5;
  const int WIDTH = 5;

  std::vector<std::vector<float> > array(WIDTH, std::vector<float>(HEIGHT));

  npy_intp dims[2] = {WIDTH, HEIGHT};
  arr = PyArray_SimpleNewFromData(2, dims, NPY_FLOAT, &array);

  return arr; 
}

BOOST_PYTHON_MODULE(sumpar) {
  using namespace boost::python;
  def("build_day", build_day, args("year", "day"));
}

【问题讨论】:

    标签: python c++ numpy boost boost-python


    【解决方案1】:

    boost::python::object 为 Python 对象提供了一个通用接口。要从PyObject* 构造一个,首先必须构造一个boost::python::handle&lt;&gt;,它本质上是一个智能指针,旨在管理引用计数的Python 对象(PyObject* 或派生类型)。人们经常在 Boost.Python 的高级代码和 Python/C API 之间的边界之间使用handle&lt;&gt;

    namespace python = boost::python;
    PyObject* py_object = get_py_object();
    python::handle<> handle(py_object);
    boost::python object(handle);
    

    请注意,handle 将共享 PyObject* 的所有权,并且在销毁期间,它将减少它正在管理的 PyObject 上的引用计数。因此,在构造过程中,重要的是要指定handle&lt;&gt;是否需要增加PyObject*的引用计数。

    如果PyObject 的引用计数已经增加,则使用:

    namespace python = boost::python;
    PyObject* py_object = ...;
    python::handle<> handle(py_object);
    python::object object(handle);
    

    如果PyObject的引用计数没有增加,句柄必须这样做,那么在构造过程中使用borrowed()函数:

    namespace python = boost::python;
    PyObject* py_object = ...;
    python::handle<> handle(python::borrowed(py_object));
    python::object object(handle);
    

    这是一个完整的例子 demonstratingPyObject* 构造一个 boost::python::object

    #include <vector>
    #include <boost/python.hpp>
    
    // Mocks...
    enum { NPY_FLOAT };
    typedef int npy_intp;
    PyObject* PyArray_SimpleNewFromData(int, npy_intp*, int, void*)
    {
      return PyString_FromString("hello world");
    }
    
    boost::python::object build_day(int year, int day)
    {
      const int HEIGHT = 5;
      const int WIDTH = 5;
    
      std::vector<std::vector<float> > array(
          WIDTH, std::vector<float>(HEIGHT));
    
      npy_intp dims[2] = {WIDTH, HEIGHT};
    
      namespace python = boost::python;
      PyObject* arr = PyArray_SimpleNewFromData(2, dims, NPY_FLOAT, &array);
      python::handle<> handle(arr);
      return python::object(handle);
    }
    
    BOOST_PYTHON_MODULE(example)
    {
      namespace python = boost::python;
      python::def("build_day", &build_day, python::args("year", "day"));
    }
    

    互动使用:

    >>> import example
    >>> day = example.build_day(1, 2);
    >>> assert(day)
    

    请注意,要创建一个最小的完整示例,上面的示例有一个模拟的PyArray_SimpleNewFromData(),它只返回 Python 字符串。请务必查阅文档以确定 PyObject* 是否被借用,以及对象及其参数之间是否存在任何生命周期要求。对于PyArray_SimpleNewFromData(),返回的PyObject*

    • 其引用计数已经增加
    • 提供给数组的底层内存的生命周期必须至少与返回的PyObject 一样长。原题中的build_day()函数不满足这个要求。

    【讨论】:

      【解决方案2】:

      我的建议是使用 boost::python 提供的变量和对象,所以如果你想将数组返回给 python 应该是使用 boost::python::dict 的好主意,类似...

      boost::python::dict arr;
      
      int i = 0;
      for (auto &item: array) {
          arr[i] = item;
          ++i;
      }
      
      return arr;
      

      【讨论】:

        猜你喜欢
        • 2011-12-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-11-17
        • 2020-11-24
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多