有没有办法将pythonappend与SWIG的新内置功能一起使用?

时间:2012-02-14 00:19:21

标签: python numpy swig

我有一个与SWIG很好地合作的小项目。特别是,我的一些函数返回std::vector,它们被转换为Python中的元组。现在,我做了很多数字,所以我只是让它们从c ++代码返回后将这些转换为numpy数组。为此,我在SWIG中使用类似下面的内容。

%feature("pythonappend") My::Cool::Namespace::Data() const %{ if isinstance(val, tuple) : val = numpy.array(val) %}

(实际上,有几个名为Data的函数,其中一些函数返回浮点数,这就是我检查val实际上是一个元组的原因。)这很有效。

但是,我还想使用现在可用的-builtin标志。对这些数据函数的调用是罕见的,并且大部分是交互式的,因此它们的缓慢不是问题,但是还有其他缓慢的循环,使用内置选项显着加速。

问题是当我使用该标志时,会自动忽略pythonappend功能。现在,Data再次返回一个元组。有什么方法我仍然可以返回numpy数组?我尝试使用打字机,但它变成了一个巨大的混乱。

编辑:

Borealid非常好地回答了这个问题。为了完整性,我包含了一些我需要的相关但略有不同的类型图,因为我通过const引用返回并使用向量向量(不要启动!)。这些是不同的,我不希望任何其他人绊倒试图找出微小的差异。

%typemap(out) std::vector<int>& {
  npy_intp result_size = $1->size();
  npy_intp dims[1] = { result_size };
  PyArrayObject* npy_arr = (PyArrayObject*)PyArray_SimpleNew(1, dims, NPY_INT);
  int* dat = (int*) PyArray_DATA(npy_arr);
  for (size_t i = 0; i < result_size; ++i) { dat[i] = (*$1)[i]; }
  $result = PyArray_Return(npy_arr);
}
%typemap(out) std::vector<std::vector<int> >& {
  npy_intp result_size = $1->size();
  npy_intp result_size2 = (result_size>0 ? (*$1)[0].size() : 0);
  npy_intp dims[2] = { result_size, result_size2 };
  PyArrayObject* npy_arr = (PyArrayObject*)PyArray_SimpleNew(2, dims, NPY_INT);
  int* dat = (int*) PyArray_DATA(npy_arr);
  for (size_t i = 0; i < result_size; ++i) { for (size_t j = 0; j < result_size2; ++j) { dat[i*result_size2+j] = (*$1)[i][j]; } }
  $result = PyArray_Return(npy_arr);
}

编辑2:

虽然不是我想要的,但使用@MONK的方法(explained here)也可以解决类似的问题。

1 个答案:

答案 0 :(得分:7)

我同意你的看法,使用typemap会有点混乱,但这是完成这项任务的正确方法。您也是对的,SWIG文档没有直接说明%pythonappend-builtin不兼容,但强烈暗示:%pythonappend 添加到Python代理类,并且Python代理类根本不存在与-builtin标志一起使用。

之前,您正在做的是让SWIG将C ++ std::vector对象转换为Python元组,然后将这些元组传递回numpy - 再次转换它们。

你真正想做的是在C级转换它们一次。

这里有一些代码可以将所有std::vector<int>个对象转换为NumPy整数数组:

%{
#include "numpy/arrayobject.h"
%}

%init %{
    import_array();
%}

%typemap(out) std::vector<int> {
    npy_intp result_size = $1.size();

    npy_intp dims[1] = { result_size };

    PyArrayObject* npy_arr = (PyArrayObject*)PyArray_SimpleNew(1, dims, NPY_INT);
    int* dat = (int*) PyArray_DATA(npy_arr);

    for (size_t i = 0; i < result_size; ++i) {
        dat[i] = $1[i];
    }

    $result = PyArray_Return(npy_arr);
}

这使用C级numpy函数来构造和返回数组。按顺序,它:

  • 确保NumPy的arrayobject.h文件包含在C ++输出文件中
  • 加载Python模块时会调用import_array(否则,所有NumPy方法都会出现段错误)
  • std::vector<int>的任何回复映射到带有typemap
  • 的NumPy数组中

此代码应放在之前%import包含返回std::vector<int>的函数的标头。除了这个限制之外,它完全是自包含的,所以它不应该为你的代码库添加过多的主观“混乱”。

如果您需要其他矢量类型,则只需更改NPY_INT和所有int*int位,否则重复上述功能。