Cython - 将指针数组转换为Python对象

11 投票
2 回答
4719 浏览
提问于 2025-04-16 13:29

好的,我快要完成这个项目了,感觉已经触手可及了。在过去的几周里,我一直在尝试创建一个Python扩展,用来通过Cython与一个用C++写的库进行交互。在这里得到了一些帮助,还有几个朋友的支持,我觉得自己已经完成了大约98%的工作。剩下的唯一问题是:我实在搞不懂怎么把一个指向无符号短整型数组的指针转换成Python对象(最好是列表)。

先说一下背景,我正在尝试与库中的一个设置回调函数的部分进行交互,这个我已经成功实现了,代码如下:

global callbackfunc

ctypedef unsigned short const_ushort "const uint16_t"

ctypedef void (*Function1)(const_ushort *data, unsigned width, unsigned height)

cdef extern from "lib.hpp":
    void SetCallback(Function1)

cdef void cSetCallback(Function1 function):
    SetCallback(function)

cdef void callcallback(const_ushort *data, unsigned width, unsigned height):
    global callbackfunc
    callbackfunc(data,width,height)

cSetCallback(callcallback)

def PySetCallback(callbackFunc):
    global callbackfunc
    callbackfunc = callbackFunc

问题出现在“callcallback”这个函数里,我遇到了一个错误:“无法将'const_ushort *'转换为Python对象”。我第一次尝试解决这个问题时,创建了一个新的Python列表,然后循环遍历数组,把每个元素放到Python列表里,像这样:

datalist = []
for i in range(width*height):
    datalist += data[i]

但是,结果是编译的Cython代码试图把一个类型定义为“const const unsigned short”,这显然是个问题。

接着我尝试了这个:

datalist = []
for i in data:
    datalist += i

结果提示我“C数组迭代需要已知的结束索引”。需要说明的是,我对C/C++了解得很少,所以大部分内容对我来说并不太明白。

总之,有没有什么有效的方法可以把这样的指针转换成Python对象(最好比循环遍历数组快,因为通常有大约57344个项目,而且这个过程对时间要求很高)

编辑: 再多说一点,正如我提到的,我在处理回调,而库中的C++函数会发送一个指向“const uint_16”数组的指针,这就是我这样定义const_ushort的原因,因为否则类型就不一致了。我无法以任何方式修改这个库。

编辑2: 看起来我搞定了。最后我不得不明确将数组转换为无符号短整型数组,而不是常量无符号短整型数组,这样我才能用非常量来索引它们。为此,我创建了另一个C++函数,像这样(是别人帮我写的,我对C++几乎一无所知):

unsigned short *convert_short(const unsigned short *test){ return const_cast<unsigned short *>(test); }

这让我在我的类中创建了“getindex”函数,并根据这个函数返回正确的值。所以,Python似乎能够正确读取这些数组,问题看起来解决了。非常感谢大家。

2 个回答

1

这可能不是你想问的,但如果你想在Cython中处理一个无符号短整型数组,然后让这个结果在Python中使用,你可以在Python那边创建一个Numpy的ndarray,然后把它传到Cython里。在Cython中,这个ndarray大致上就像一个多维数组。这样,你就能享受到Python的内存管理、方便的索引语法,以及在Cython中处理真实数组的速度。

这里有一个来自我Project Euler解决方案的例子

5
ctypedef unsigned short const_ushort "const uint16_t"

这段话的意思是,使用 const 会让它在 C 代码中出现,所以你会看到 typedef unsigned short const uint16_t,但这里的 const 其实没有什么意义。(而且它还重新定义了标准类型 uint16_t,这是不应该的。)

你可能会更成功地使用

ctypedef unsigned short *ushort_p "ushort_p"

出于某种原因,Cython 似乎不识别 C 语言中的 const 关键字。你可能需要在你的回调函数周围加一个包装器来处理 const 的问题。

如果你想把指针转换成 Python 对象,可以把它放在一个 cdef class 里。确保这个类记录了高度和宽度,因为这些信息在 C 数组中是没有记录的。

撰写回答