你似乎已经解决了你的问题,但我还是想回答这个问题。第一个原因是我想尝试一下,第二个原因是我认为你在内存管理方面有一些问题,我想指出一下。
我们将包装以下简单的C接口:
//creator.h
typedef struct {
int mult;
int add;
} Result;
typedef struct {
int size;
Result *arr;
} ResultArray;
ResultArray create(int size, int *input){
//whole file at the end of the answer
}
我们的包装pyx文件如下所示:
#result_import.pyx (verion 0)
cdef extern from "creator.h":
ctypedef struct Result:
int mult
int add
ctypedef struct ResultArray:
int size
Result *arr
ResultArray create(int size, int *input)
def create_structs(int[::1] input_vals):
pass
int[::1]
)传递我的输入数组,这有两个优点:
-
numpy
,
array
由于Python3),使用numpy数组更灵活
-
确保(通过
[::1]
在测试脚本中,我使用numpy,但也可以使用内置数组:
#test.py
import result_import
import numpy as np
a=np.array([1,2,3],dtype='int32')
result=result_import.create_structs(a)
for i,el in enumerate(result):
print i, ": mult:", el.mult, " add:", el.add
现在一切都还没有开始,但一切都准备好了。
第一种情况:
#result_import.pyx (verion 1)
#from cpython cimport array needed for array.array in Python2
from libc.stdlib cimport free
....
class PyResult:
def __init__(self, mult, add):
self.mult=mult
self.add=add
def create_structs(int[::1] input_vals):
cdef ResultArray res=create(len(input_vals), &input_vals[0])
try:
lst=[]
for i in range(res.size):
lst.append(PyResult(res.arr[i].mult, res.arr[i].add))
finally:
free(res.arr)
return lst
我将整个数据转换为python对象,并使用一个简单的列表。非常简单,但有两件事值得注意:
-
内存管理:我负责释放res.arr中分配的内存
try...finally
-
仅将此指针设置为是不够的
NULL
,我必须打电话
free
-功能。
现在我们的
test.py
第二种情况:
然而,如果我只需要其中的一些元素并将它们全部转换,那就是效率低下。此外,我在内存中保留了所有元素两次(至少一段时间)-这是天真方法的缺点。所以我想创造
PyResult
-在程序中稍后的某个地方按需访问对象。
让我们写一个包装器列表:
#result_import.pyx (verion 2)
...
cdef class WrappingList:
cdef int size
cdef Result *arr
def __cinit__(self):
self.size=0
self.arr=NULL
def __dealloc__(self):
free(self.arr)
print "deallocated"#just a check
def __getitem__(self, index):
if index<0 or index>=self.size:
raise IndexError("list index out of range")
return PyResult(self.arr[index].mult, self.arr[index].add)
def create_structs(int[::1] input_vals):
cdef ResultArray res=create(len(input_vals), &input_vals[0])
lst=WrappingList()
lst.size, lst.arr=res.size, res.arr
return lst
所以这个班
WrappingList
其行为非常类似于列表,保留整个C数组而不复制并创建
PyResult公司
-对象仅在需要时。值得一提的是:
-
__dealloc__
当
WrapperingList
-对象被销毁-这是我们释放C代码提供给我们的内存的地方。在…的结尾
测验py公司
我们应该看到“解除分配”,否则就会出问题。。。
-
__getitem__
第三种情况:
Python代码不仅应该读取结果,还应该对其进行更改,因此更改后的数据可以传递回C代码。为此,让我们
PyResult公司
代理人:
#result_import.pyx (verion 3, last)
...
cdef class PyResult:
cdef Result *ptr #ptr to my element
def __init__(self):
self.ptr=NULL
@property
def mult(self):
return self.ptr.mult
@mult.setter
def mult(self, value):
self.ptr.mult = value
@property
def add(self):
return self.ptr.add
@add.setter
def add(self, value):
self.ptr.add = value
cdef class WrappingList:
...
def __getitem__(self, index):
if index>=self.size:
raise IndexError("list index out of range")
res=PyResult()
res.ptr=&self.arr[index]
return res
-对象持有一个指向相应元素的指针,可以在C数组中直接对其进行更改。然而,我要提到的是,存在一些陷阱:
-
PyResult公司
不应活得比父母长-
包装英语
PyResult公司
-类。
-
访问元素(
add
mult
)成本相当高,因为每次都必须创建、注册然后删除一个新的Python对象。
#test.py(second version)
import result_import
import numpy as np
a=np.array([1,2,3],dtype='int32')
result=result_import.create_structs(a)
for i,el in enumerate(result):
print i, ": mult:", el.mult, " add:", el.add
el.mult, el.add=42*i,21*i
# now print changed values:
for i,el in enumerate(result):
print i, ": mult:", el.mult, " add:", el.add
附件:
马虎的
creator.h
-需要检查
malloc
//creator.h
typedef struct {
int mult;
int add;
} Result;
typedef struct {
int size;
Result *arr;
} ResultArray;
ResultArray create(int size, int *input){
ResultArray res;
res.size=size;
res.arr=(Result *)malloc(size*sizeof(Result));//todo: check !=0
for(int i=0;i<size;i++){
res.arr[i].mult=2*input[i];
res.arr[i].add=2+input[i];
}
return res;
}
设置。py:
from distutils.core import setup, Extension
from Cython.Build import cythonize
setup(ext_modules=cythonize(Extension(
name='result_import',
sources = ["result_import.pyx"]
)))