我正在尝试创建一个Python模块,该模块将从C共享库返回的C结构数组转换为Numpy数组。我是Numpy和Cython的新手(但是已经有很长时间的C语言了),所以我一直在学习。 一些注意事项: 1)名为HBtrial的C共享库位于另一个目录中 2)C代码calloc()的内存,填充结构并返回指向结构数组的指针 3)我需要返回的数组是Numpy数组(最好是结构化数组,然后可能会转换为Pandas Dataframe)
尝试了许多事情之后,通过执行以下操作,我得到了最远的帮助(包括让.pyx文件进行编译)。
trial.pyx
import cython
from cpython.ref cimport PyTypeObject
cimport numpy as np
import numpy as np
cimport HBtrial
cdef extern from "numpy/ndarrayobject.h":
object PyArray_NewFromDescr(PyTypeObject *subtype,
np.dtype newdtype,
int nd,
np.npy_intp* dims,
np.npy_intp* strides,
void* data,
int flags,
object parent)
np.import_array()
class MyStruct(object):
dtype_mystruct = np.dtype ([('item', 'S16'),
('date', 'S16'),
('val1', 'u1'),
('val2', 'u1'),
('val3', 'i2')
])
def __init__(self):
pass
def return_dtype(self):
return self.dtype_mystruct
@cython.boundscheck(False)
def return_values(self):
cdef int rows
cdef HBtrial.MYSTRUCT *arr = HBtrial.return_values(&rows)
print arr[1]
print "npy array"
cdef np.npy_intp dims = rows
nparr = np.PyArray_NewFromDescr(np.ndarray,
self.dtype_mystruct,
1,
dims,
<object>NULL,
<object><void *>arr,
0,
<object>NULL)
print nparr[1]
return nparr
它可以编译,但是随后我尝试在小型Python脚本中使用它,如下所示:
try.py:
#!/usr/bin/env python
import sys
import os
import numpy as np
from trial import MyStruct
def main():
mystruct = MyStruct()
dt = mystruct.return_dtype()
print dt
arr = mystruct.return_values()
print arr
if __name__ == "__main__":
main()
运行它时,它可以很好地打印出“ print dt”行,但是出现以下错误:
Traceback (most recent call last):
File "./try.py", line 18, in <module>
main()
File "./try.py", line 14, in main
arr = mystruct.return_values()
File "trial.pyx", line 43, in trial.MyStruct.return_values (trial.c:1569)
nparr = np.PyArray_NewFromDescr(np.ndarray,
AttributeError: 'module' object has no attribute 'PyArray_NewFromDescr'
如何克服此错误? 我觉得我可能缺少一些基本知识。有任何想法吗?如果我完全不了解自己的方法,也请告诉我。
以下是其他文件,如果有帮助的话:
trial.pxd:
from libc.stdint cimport int8_t, int16_t, uint8_t, uint16_t
cdef extern from "HBtrial.h" nogil:
ctypedef packed struct MYSTRUCT:
char item[16];
char date[16];
uint8_t val1;
uint8_t val2;
int16_t val3;
cdef MYSTRUCT *return_values(int *rows)
HBtrial.h:
#ifndef HBTRIAL_H
#define HBTRIAL_H
typedef struct {
char item[16];
char date[16];
uint8_t val1;
uint8_t val2;
int16_t val3;
} MYSTRUCT;
MYSTRUCT *return_values(int *rows);
#endif
HBtrial.c:
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include "HBtrial.h"
MYSTRUCT *return_values(int *rows)
{
int i;
MYSTRUCT *arr;
int numrows = 5;
arr = calloc(numrows, sizeof(MYSTRUCT));
for (i=0; i < numrows; i++) {
sprintf(arr[i].item, "row%d", i);
sprintf(arr[i].date, "201908100%d", i+1);
arr[i].val1 = i+2;
arr[i].val2 = i+i;
arr[i].val3 = i*i;
}
*rows = numrows;
return(arr);
}
HBtrial.c和HBtrial.h位于/ home / xxxx / lib / try3中,并被编译到共享库“ libHBtrial.so”中。
setup.py:
from distutils.core import setup
from distutils.extension import Extension
from Cython.Build import cythonize
import numpy as np
trial = Extension(
name="trial",
sources=["trial.pyx"],
extra_compile_args=["-std=c99"],
libraries=["HBtrial"],
library_dirs=["/home/xxxx/lib/try3"],
include_dirs=[np.get_include(), "/home/xxxx/lib/try3"]
)
setup(
name="trial",
ext_modules=cythonize([trial])
)
如果有更好的方法,我也会对此感兴趣。例如,我尝试了其他方法,例如将返回的数组转换为Cython类型的memoryview,或使用np.frombuffer(),但始终会收到一个错误,提示它"Cannot convert MYSTRUCT * to"
一个memoryview或python对象或其他任何东西。
答案 0 :(得分:0)
好吧,我终于开始工作了。感谢@ead的评论和我已经在考虑的事情,我需要对PyArray_FromNewDescr
进行调用并更改几个参数。
作为“ cdef extern ...”块的一部分,我添加了:
PyTypeObject PyArray_Type
然后对例程的调用变为:
cdef np.ndarray nparr = PyArray_NewFromDescr(&PyArray_Type,
self.dtype_mystruct,
1,
&dims,
NULL,
<void *>arr,
0,
<object>NULL)
现在,我可以从例程返回时以适当的值打印数组。
至于内存泄漏,根据我发现的其他帖子,我应该可以通过在返回数组之前在数组上设置OWNDATA标志来解决此问题,对吗?