栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

使用Cython在Python和C++间互传大小事先未知的numpy数组

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

使用Cython在Python和C++间互传大小事先未知的numpy数组

从C++传到Python

常见的教程如这个问题及回答是将大小已知的numpy数组传入传出C++,如确定会从C++传出大小为 M × N M times N M×N的矩阵。方法简单讲就是在Python端分配一个大小为 M × N M times N M×N的矩阵,把指向这个矩阵的指针传给C++,C++负责修改矩阵的内容,结束后矩阵就自动“传回”了。

然而有时我们事先不知道从C++传回的矩阵是多大,这时我们可以用这个回答所提及的技术,即从C++传回std::vector,然后在Python端把它无拷贝地转成numpy数组。

例子:从C++传回 M × 2 M times 2 M×2大小的矩阵, M M M在Python端未知。例子主要来源于网络,但我稍微换了一下应用,并修改了里面的谬误。

doit.h:

#ifndef _DOIT_H_
#define _DOIT_H_
#include 
std::vector arange2d();
#endif

doit.cpp:

#include "doit.h"
std::vector arange2d() {
	std::vector arr(10);
	long x = 0;
	for (auto i = arr.begin(); i != arr.end(); ++i) {
		*i = x++;
	}
	return arr;
}

fast.pyx:

from libcpp.vector cimport vector

cdef extern from 'doit.h':
    vector[long] arange2d()

cdef class ArrayWrapper:
    cdef vector[long] v
    cdef Py_ssize_t shape[2];
    cdef Py_ssize_t strides[2];

    def set_data(self, vector[long]& data):
        self.v.swap(data)  # 注(1)

    def __getbuffer__(self, Py_buffer *buf, int flags):
        self.shape[0] = self.v.size() // 2
        self.shape[1] = 2
        self.strides[0] = self.shape[1] * sizeof(long)
        self.strides[1] = sizeof(long)

        # 注(2)
        buf.buf =  self.v.data()
        buf.format = 'l'  # 注(3)
        buf.internal = NULL
        buf.itemsize =  sizeof(long)
        buf.len = self.v.size() * sizeof(long)
        buf.ndim = 2
        buf.obj = self
        buf.readonly = 0
        buf.shape = self.shape
        buf.strides = self.strides
        buf.suboffsets = NULL

def pyarange2d():
    cdef vector[long] arr = arange2d()
    cdef ArrayWrapper wrapper = ArrayWrapper()
    wrapper.set_data(arr)
    return np.asarray(wrapper)
  • 注(1):std::vector::swap完成了无拷贝传值,另一种方法是用std::move,不过那需要cdef extern from '' namespace 'std' nogil: vector[long] move(vector[long]),应该是这样,不过我没试过
  • 注(2):numpy的Buffer Protocol见此处,里面讲了buf需要设置哪些属性
  • 注(3):buf.format如何设置见此处

至于从C++传回Python的多维数组有两个及以上的维度不知道的话(已知维度总数ndim),网络上没找到答案,但我是这么做的:

  1. 传给C++一个指向Py_ssize_t类型、长度为ndim的数组(即待传回数组的shape)的指针
  2. C++传回一个std::vector并修改shape元素为合适的值
  3. 按照shape及std::vector的元素类型填写buf的属性,完成std::vector到numpy数组的转换
从Python传到C++

这应该已经耳熟能详了,我就不在此赘述了。不过有一点需要注意。传double数组时没问题,各平台double都对应numpy.float64。传int数组时需注意,Windows下对应numpy.int32、Linux/Mac下对应numpy.int64。所以直接用传double数组的方法传int数组会报这个错:

Cannot assign type 'int_t *' to 'int *'

见这个问题(就是我提的)。目前我还没有优雅的解决方法。我笨拙的方法(受ead的启发)(请对照着“这个问题”看)如下:把所有的int全替换为int64_t(或int32_t,一致就行),例如int * => int64_t *、np.int_t => np.int64_t,然后在dotit.h包含头文件的地方加上#include ,在q.pyx头部加上from libc.stdint cimport int64_t。应该就可以编译了。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/950386.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号