Boost :: Python API не позволяет вам делать это напрямую.Вы можете использовать Numpy C API для этого.Получить доступ к базовому PyObject*
можно с помощью метода ptr()
из boost::python::object
.Доступ к данным можно получить, используя PyArray_DATA(PyObject*)
.
. Вот пример, который умножает массив 2d Numpy на число.У меня возникли проблемы с поиском компиляции этого в Mac OS (то есть там, где находятся заголовки Numpy), поэтому я добавлю это здесь: запуск
import numpy; print numpy.get_include()
в Python дает правильный путь включения,Например, вы можете использовать это в модуле поиска cmake (см., Например, this ; вам придется установить переменную PYTHON_EXECUTABLE
самостоятельно).
Обновление: Я изменил код для обработки случая, когда входной массив не является смежным - это происходит, например, всякий раз, когда вы используете срез, как в arr[::2, ::2]
.Самый простой способ справиться с этим - использовать PyArray_FROM_OTF
;заметьте, однако, что это сделает копию массива, если массив уже не находится в смежной форме.Преимущество этого подхода состоит в том, что он может обрабатывать случай, когда входные данные представляют собой вложенную последовательность любого типа (например, список списков).
Если ваш массив настолько велик, что вы предпочитаете избегать копированияиз этого вы можете использовать PyArray_STRIDES
, чтобы получить оперативную информацию, которая поможет вам получить доступ к данным.С http://docs.scipy.org/doc/numpy/reference/generated/numpy.ndarray.strides.html,
Смещение байта элемента (i[0], i[1], ..., i[n])
в массиве a
составляет:
offset = sum(np.array(i) * a.strides)
(обратите внимание, чтоэто смещение байт , поэтому вы должны добавить его к результату PyArray_BYTES
до того, как приведете к окончательному типу данных, также это не поможет, если массив будет неправильно выровненили если он имеет неправильный порядок байтов)
Вот пример кода:
#include <boost/python.hpp>
#include <exception>
#include <numpy/arrayobject.h>
using namespace boost::python;
struct WrongSizeError : public std::exception {
const char* what() const throw() { return "Unsupported array size."; }
};
struct WrongTypeError : public std::exception {
const char* what() const throw() { return "Unsupported array type."; }
};
// Boost::Python needs the translators
void translate_sz(const WrongSizeError& e)
{
PyErr_SetString(PyExc_RuntimeError, e.what());
}
void translate_ty(const WrongTypeError& e)
{
PyErr_SetString(PyExc_RuntimeError, e.what());
}
// multiply matrix of double (m) by f
object multiply(numeric::array m, double f)
{
PyObject* m_obj = PyArray_FROM_OTF(m.ptr(), NPY_DOUBLE, NPY_IN_ARRAY);
if (!m_obj)
throw WrongTypeError();
// to avoid memory leaks, let a Boost::Python object manage the array
object temp(handle<>(m_obj));
// check that m is a matrix of doubles
int k = PyArray_NDIM(m_obj);
if (k != 2)
throw WrongSizeError();
// get direct access to the array data
const double* data = static_cast<const double*>(PyArray_DATA(m_obj));
// make the output array, and get access to its data
PyObject* res = PyArray_SimpleNew(2, PyArray_DIMS(m_obj), NPY_DOUBLE);
double* res_data = static_cast<double*>(PyArray_DATA(res));
const unsigned size = PyArray_SIZE(m_obj); // number of elements in array
for (unsigned i = 0; i < size; ++i)
res_data[i] = f*data[i];
return object(handle<>(res)); // go back to using Boost::Python constructs
}
BOOST_PYTHON_MODULE(test)
{
// register exception translators
register_exception_translator<WrongSizeError>(&translate_sz);
register_exception_translator<WrongTypeError>(&translate_ty);
// tell Boost::Python under what name the array is known
numeric::array::set_module_and_type("numpy", "ndarray");
def("multiply", multiply);
// initialize the Numpy C API
import_array();
}