/ / PYBIND11: Erstellen Sie numpy Ansicht von Daten - Python, C ++, PYBIND11

PYBIND11: Erstellen Sie eine nackte Ansicht von Daten - Python, C ++, PYBIND11

Ich möchte eine anschauliche Ansicht der Daten in einer C ++ - Klasse erstellen.

Aber das Folgende macht eine Kopie statt einer Ansicht.

Der Python-Test:

import _cpp
a = _cpp.A()
print(a)
a.view()[:] = 100  # should make it all 100.
print(a)

Ergebnis:

40028064 0 0 0  // Fail: Modifying a.mutable_data() in C++ doesn"t
//       change _data[4]
40028064 0 0 0  // Fail: Modifying a.view() in Python 3 doesn"t
//       change data in a

Die C ++ Zeile a.mutable_data()[0] = -100; ändert nicht das 0. Element zu -100. Dies zeigt py::array_t<int> a(4, &_data[0]); Erstellt eine Kopie anstelle einer Ansicht von int _data[4];

Ändern des Arrays a.view() ändert die Daten nicht a bis 100s. Dies zeigt, dass a.view() ist eine Kopie anstelle einer Ansicht der Daten in a.

main.cpp:

#include <iostream>
#include "pybind11/pybind11.h"
#include "pybind11/numpy.h"

namespace py = pybind11;
class A {
public:
A() {}
std::string str() {
std::stringstream o;
for (int i = 0; i < 4; ++i) o << _data[i] << " ";
return o.str();
}
py::array view() {
py::array_t<int> a(4, &_data[0]);
a.mutable_data()[0] = -100;
return a;
}
int _data[4];
};

PYBIND11_MODULE(_cpp, m) {
py::class_<A>(m, "A")
.def(py::init<>())
.def("__str__", &A::str)
.def("view", &A::view, py::return_value_policy::automatic_reference);
}

CMakeLists.txt:

cmake_minimum_required(VERSION 3.9)
project(test_pybind11)

set(CMAKE_CXX_STANDARD 11)

# Find packages.
set(PYTHON_VERSION 3)
find_package( PythonInterp ${PYTHON_VERSION} REQUIRED )
find_package( PythonLibs ${PYTHON_VERSION} REQUIRED )

# Download pybind11
set(pybind11_url https://github.com/pybind/pybind11/archive/stable.zip)

set(downloaded_file ${CMAKE_BINARY_DIR}/pybind11-stable.zip)
file(DOWNLOAD ${pybind11_url} ${downloaded_file})
execute_process(COMMAND ${CMAKE_COMMAND} -E tar xzf ${downloaded_file}
SHOW_PROGRESS)
file(REMOVE ${downloaded_file})

set(pybind11_dir ${CMAKE_BINARY_DIR}/pybind11-stable)
add_subdirectory(${pybind11_dir})
include_directories(${pybind11_dir}/include)

# Make python module
pybind11_add_module(_cpp main.cpp)

Antworten:

1 für die Antwort № 1

Nach dem Kommentar in Frage 308 das sagt py::cast(self), Ich versuche py::cast(*this).

Das funktioniert. Ich fühle mich ein wenig unwohl über die Ungültigkeit der Ansichten, aber numpy macht das genauso.

Python-Test:

import _cpp
import numpy as np
a = _cpp.A()
print(a)
a.view()[:] = 100  # should make it all 100.
print(a)

Testergebnis:

1480305816 32581 19420784 0 // original data of `a`
100 100 100 100 // It works: changing `a.view()` changes data of `a`.

main.cpp:

#include <iostream>
#include "pybind11/pybind11.h"
#include "pybind11/numpy.h"

namespace py = pybind11;
class A {
public:
A() {}
std::string str() {
std::stringstream o;
for (int i = 0; i < 4; ++i) o << _data[i] << " ";
return o.str();
}
py::array view() {
return py::array(4, _data, py::cast(*this));  // <---
}
int _data[4];
};

PYBIND11_MODULE(_cpp, m) {
py::class_<A>(m, "A")
.def(py::init<>())
.def("__str__", &A::str)
.def("view", &A::view, py::return_value_policy::reference_internal);
}

ich benutze reference_internal um die Lebenszeit von a.view() binden an die Lebenszeit von a.


Die Ansicht wird ungültig, wenn das übergeordnete Objekt gelöscht wird.

Nach dem Löschen a Im Python-Test sammelt Python Müll die Daten von a auf unbestimmte Zeit. Dies bedeutet, wenn ich vorher die Ansicht gespeichert habe b = a.view(), b wird nachher ungültig gemacht a ist gelöscht.

Ich versuche es zu machen a._data ein numerisches Array auf der C ++ Seite, aber das hilft nicht beim Ungültigmachen.

main.cpp:

class A {
public:
A() : _data(4, new int[4]) {}
std::string str() {
std::stringstream o;
for (int i = 0; i < 4; ++i) o << _data.data()[i] << " ";
return o.str();
}
py::array view() {
return py::array(4, _data.data(), py::cast(*this));
}
py::array_t<int> _data;
};

Python-Test:

import _cpp
import numpy as np
a = _cpp.A()
print(a)
a.view()[:] = 100  # should make it all 100.
b = a.view()
print("b is base?", b.base is None)
del a
print("b is base after deleting a?", b.base is None)

c = np.zeros(4)
print("c is base?", c.base is None)
d = c.view()
print("d is base?", d.base is None)
del c
print("d is base after deleting c?", d.base is None)

Ergebnis:

-6886248 32554 16092080 0
// c++ code"s management of views
b is base? False
b is base after deleting a? False
// numpy"s management of views
c is base? True
d is base? False
d is base after deleting c? False

Es sieht so aus, als wenn das Basisnumpy-Array gelöscht wird, die Eigentümerschaft des Speichers wird nicht in eine der Ansichten übertragen. Dasselbe gilt für die C ++ - Klasse. Ich denke, ich werde bei der vorherigen Lösung bleiben.