Cython:从参考获得时,Numpy 数组缺少两个第一个元素
Posted
技术标签:
【中文标题】Cython:从参考获得时,Numpy 数组缺少两个第一个元素【英文标题】:Cython: Numpy array missing two first elements when obtained from reference 【发布时间】:2018-01-15 12:31:22 【问题描述】:这是最奇怪的错误,我试图从一个返回 reference 向量的 c++ 函数中获取一个 numpy 数组,整个使用 Cython 包装。
我可以让它返回 vector<int>
而不是 vector<int>&
,但我想了解使用引用时发生了什么。这是一种重现错误的方法:
cmyclass.h
#ifndef MYCLASS_H
#define MYCLASS_H
#include <vector>
#include <string>
namespace vec
class IntVector
private:
std::vector<int> vec;
public:
IntVector();
virtual ~IntVector();
std::vector<int>& get_vec(); #return a reference !
;
#endif
cmyclass.cc
#include "cmyclass.h"
#include <iostream>
using namespace vec;
IntVector::IntVector()
for(int i=10; i<20; ++i)
vec.push_back(i);
IntVector::~IntVector()
std::vector<int>& IntVector::get_vec()
std::vector<int> buff;
buff.reserve(vec.size());
for(int i=0; i<vec.size(); ++i)
buff.push_back(vec[i]);
return buff;
myclass.pyx
import numpy as np
cimport numpy as np
from libcpp.vector cimport vector
cdef extern from "cmyclass.h" namespace "vec":
cdef cppclass IntVector:
IntVector() except +
vector[int]& get_vec()
cdef class IntVec:
cdef IntVector* _thisptr
def __cinit__(self):
self._thisptr = new IntVector()
def __dealloc__(self):
del self._thisptr
def __init__(self):
pass
def get_vec(self):
cdef vector[int] buff;
buff = self._thisptr.get_vec();
return np.asarray(buff)
setup.py
from distutils.core import setup
from Cython.Build import cythonize
from distutils.extension import Extension
sourcefiles = ['myclass.pyx', 'cmyclass.cc']
compile_opts = ['-std=c++11']
ext=[Extension('*',
sourcefiles,
extra_compile_args=compile_opts,
language='c++')]
setup(
ext_modules=cythonize(ext)
)
您可以使用python setup.py build_ext --inplace
进行编译
用例
>>> import myclass
>>> vec = myclass.IntVec()
>>> vec.get_vec()
array([ 0, 0, 12, 13, 14, 15, 16, 17, 18, 19])
您可以看到前两个值设置为零(它们应该是 10 和 11)!如果我们返回 vector<int>
而不是对 vector<int>
的引用,则代码会正常工作。
知道为什么会这样吗?
编辑:最终解决方案
将向量作为参数传递。
cmyclass.h
#ifndef MYCLASS_H
#define MYCLASS_H
#include <vector>
#include <string>
namespace vec
class IntVector
private:
std::vector<int> vec;
public:
IntVector();
virtual ~IntVector();
void get_vec(std::vector<int>&);
;
#endif
cmyclass.cc
#include "cmyclass.h"
#include <iostream>
using namespace vec;
IntVector::IntVector()
for(int i=10; i<20; ++i)
vec.push_back(i);
IntVector::~IntVector()
void IntVector::get_vec(std::vector<int>& buff)
buff.reserve(vec.size());
for(int i=0; i<vec.size(); ++i)
buff.push_back(vec[i]);
return buff;
myclass.pyx
import numpy as np
cimport numpy as np
from libcpp.vector cimport vector
cdef extern from "cmyclass.h" namespace "vec":
cdef cppclass IntVector:
IntVector() except +
void get_vec(vector[int]&)
cdef class IntVec:
cdef IntVector* _thisptr
def __cinit__(self):
self._thisptr = new IntVector()
def __dealloc__(self):
del self._thisptr
def __init__(self):
pass
def get_vec(self):
cdef vector[int] buff;
self._thisptr.get_vec(buff);
return np.asarray(buff)
setup.py
from distutils.core import setup
from Cython.Build import cythonize
from distutils.extension import Extension
sourcefiles = ['myclass.pyx', 'cmyclass.cc']
compile_opts = ['-std=c++11']
ext=[Extension('*',
sourcefiles,
extra_compile_args=compile_opts,
language='c++')]
setup(
ext_modules=cythonize(ext)
)
【问题讨论】:
您的 c++ 代码返回对局部变量的引用,这几乎是必然的灾难。可能有一个关于它的编译器警告...... 您的函数应该是const std::vector<int>& get_vec() const return vec;
或 std::vector<int>& get_vec() return vec;
或 std::vector<int> get_vec()return vec;
,具体取决于您想要实现的目标,但不返回对临时对象的引用,正如 DavidW 指出的那样。
你是对的!我通过在.pyx
文件中声明向量并将引用作为参数传递来解决了这个问题。还有一个问题:np.asarray
是在创建数组中数据的副本吗?理想情况下,我想避免复制数据。
np.asarray
仅在需要时复制。你可以很容易地自己检查——写下相关的内存地址,看看它们是否匹配。
它肯定会复制,而且可能会复制两次(首先复制到 Python 列表,然后复制到数组)
【参考方案1】:
您的主要目标似乎是让 numpy 使用在 C++ 向量中分配的内存。为此,您最好为IntVec
实现缓冲区协议。 Cython 文档提供了一个 Matrix class based around a vector 的示例,您可以对其进行简化(因为您的案例只有一维)。您真正需要做的就是创建函数__getbuffer__
和__releasebuffer__
(后者可以是空白,如示例文档中所示)。 (我不认为在此处复制/粘贴文档有很大的价值)
这样做可以让您将IntVec
直接传递给np.asarray
。生成的 numpy 数组将使用 IntVec
进行存储,并保留对 IntVec
的引用以确保它不会被删除。您还可以在此类中使用 Cython 内存视图(如果有帮助的话)。
【讨论】:
以上是关于Cython:从参考获得时,Numpy 数组缺少两个第一个元素的主要内容,如果未能解决你的问题,请参考以下文章