看起来唯一真正的解决方案是基于pybuffer.i
构建一个可以从C++复制到现有缓冲区的东西。如果您将此添加到SWIG包含文件中:
%insert("python") %{
import numpy as np
%}
%inline %{
#include <algorithm>
template < typename Container_T >
void copy_to_buffer(
const Container_T& field,
typename Container_T::value_type* buffer,
typename Container_T::size_type length
)
{
if (length == field.size()) {
std::copy( field.begin(), field.end(), buffer );
}
}
%}
%define TYPEMAP_COPY_TO_BUFFER(CLASS...)
%typemap(in) (CLASS::value_type* buffer, CLASS::size_type length)
(int res = 0, Py_ssize_t size_ = 0, void *buffer_ = 0) {
res = PyObject_AsWriteBuffer($input, &buffer_, &size_);
if ( res < 0 ) {
PyErr_Clear();
%argument_fail(res, "(CLASS::value_type*, CLASS::size_type length)",
$symname, $argnum);
}
$1 = ($1_ltype) buffer_;
$2 = ($2_ltype) (size_/sizeof($*1_type));
}
%enddef
%define ADD_NUMPY_ARRAY_INTERFACE(PYVALUE, PYCLASS, CLASS...)
TYPEMAP_COPY_TO_BUFFER(CLASS)
%template(_copy_to_buffer_ ## PYCLASS) copy_to_buffer< CLASS >;
%extend CLASS {
%insert("python") %{
def __array__(self):
"""Enable access to this data as a numpy array"""
a = np.ndarray( shape=( len(self), ), dtype=PYVALUE )
_copy_to_buffer_ ## PYCLASS(self, a)
return a
%}
}
%enddef
然后你可以使用以下方法使一个容器能够被"Numpy"使用:
%template(DumbVectorFloat) DumbVector<double>;
ADD_NUMPY_ARRAY_INTERFACE(float, DumbVectorFloat, DumbVector<double>);
然后在Python中,只需执行以下操作:
# dvf is an instance of DumbVectorFloat
import numpy as np
my_numpy_array = np.asarray( dvf )
这只有一个Python <--> C++翻译调用的开销,而不是典型长度为N的数组所产生的N个调用。
这段代码的稍微完整版本是我在github上的PyTRT项目的一部分。
__array_interface__
只是一个普通的字典,其中包含普通类型。不需要使用任何Numpy头文件进行编译。忽略将其称为“遗留”的注释。我以为我已经删除了它。如果您愿意,可以实现PEP 3118缓冲区接口,但这更容易。 - Robert Kern