2

我想在 C++ 类中创建数据的 numpy 视图。

但是下面的内容是复制而不是视图。

蟒蛇测试:

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

结果:

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

C++ 行a.mutable_data()[0] = -100;不会将第 0 个元素更改为 -100。这显示py::array_t<int> a(4, &_data[0]);创建副本而不是视图int _data[4];

修改数组a.view()不会将数据更改a为 100 秒。这表明这a.view()是一个副本,而不是 中数据的视图a

主.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)
4

1 回答 1

1

按照问题 308中的评论说py::cast(self),我尝试py::cast(*this)

这样可行。我对视图无效感到有些不安,但 numpy 也是这样做的。

蟒蛇测试:

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

测试结果:

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

主.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);
}

reference_internal用来使生命时间a.view()与生命时间相关联a


删除父对象时视图无效。

在python测试中删除后,python会无限期a的对数据进行垃圾收集。a这意味着如果我以前存储视图b = a.view(),则在删除b后无效。a

我尝试a._data在 C++ 端创建一个 numpy 数组,但这无助于失效。

主.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;
};

蟒蛇测试:

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)

结果:

-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

看起来当基本 numpy 数组被删除时,内存的所有权不会转移到其中一个视图。C++ 类也是如此。我想我会坚持以前的解决方案。

于 2018-03-08T20:19:03.037 回答