我有一个与 SWIG 完美配合的小项目。特别是,我的一些函数返回 std::vector
s,它们在 Python 中被翻译成元组。现在,我做了很多数字,所以我只是让 SWIG 在它们从 c++ 代码返回后将它们转换为 numpy 数组。为此,我在 SWIG 中使用了类似以下的内容。
%feature("pythonappend") My::Cool::Namespace::Data() const %{ if isinstance(val, tuple) : val = numpy.array(val) %}
(实际上,有几个名为 Data 的函数,其中一些返回 float ,这就是为什么我检查 val
实际上是一个元组的原因。)这很好用。
但是,我还想使用现在可用的 -builtin
标志。对这些 Data 函数的调用很少见,而且大多是交互式的,因此它们的速度很慢不是问题,但还有其他慢速循环可以通过内置选项显着加快。
问题在于,当我使用该标志时,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) 来解决。
最佳答案
我同意你的看法,使用 typemap
有点困惑,但这是完成这项任务的正确方法。你也是对的,SWIG 文档没有直接说 %pythonappend
与 -builtin
不兼容,但强烈暗示:%pythonappend
添加到 Python 代理类,与 -builtin
一起使用的 Python 代理类根本不存在标志。
之前,您所做的是让 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++ 输出文件中 - 原因
import_array
在 Python 模块加载时调用(否则所有 NumPy 方法都会出现段错误) - 映射
std::vector<int>
的任何返回进入带有typemap
的 NumPy 数组
这段代码应该放在之前你%import
包含返回 std::vector<int>
的函数的 header .除了这个限制之外,它是完全独立的,所以它不应该给你的代码库添加太多主观的“困惑”。
如果您需要其他矢量类型,您只需更改 NPY_INT
和所有 int*
和 int
位,否则复制上面的函数。
关于python - 有什么方法可以将 pythonappend 与 SWIG 的新内置功能一起使用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9270052/