在Python中使用ctypes访问数组

时间:2015-07-25 21:46:39

标签: python c numpy ctypes dllexport

我正在用C编写一个ode-solver,导出到一个Windows DLL和一个DLL的Python包装器。我很习惯Python,但我也是C和ctypes的初学者。

受接受的答案here启发的修改后的解决方案如下:

C代码

/* my_clib.c */

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

struct data {
  int nr_steps;
  double dt;
  double* t;
  double* x;
  double t0, x0;
 };

double fun_to_integrate(double t, double y){
    return (y - t);
 }

double rk4(double t, double y, double dt){
    double  k1 = dt * fun_to_integrate(t, y),
            k2 = dt * fun_to_integrate(t + dt / 2, y + k1 / 2),
            k3 = dt * fun_to_integrate(t + dt / 2, y + k2 / 2),
            k4 = dt * fun_to_integrate(t + dt, y + k3);
    return y + (k1 + 2 * k2 + 2 * k3 + k4) / 6;
}

__declspec(dllexport) void my_fun(struct data* pointer){
    int i;
    double dt;
    dt = pointer->dt;
    pointer->t[0] = pointer->t0;
    pointer->x[0] = pointer->x0;
    for(i = 1; i < pointer->nr_steps; i++){
        pointer->t[i] = dt*i + pointer->t0;
        pointer->x[i] = rk4(pointer->t[i-1], pointer->x[i-1], dt);
    }
 }

使用相应的Python文件

# my_python.py
import ctypes
import numpy as np

class DATA(ctypes.Structure):
    _fields_ = [
                ('nr_steps', ctypes.c_int),
                ('dt', ctypes.c_double),
                ('t', ctypes.POINTER(ctypes.c_double)),
                ('x', ctypes.POINTER(ctypes.c_double)),
                ('t0', ctypes.c_double),
                ('x0', ctypes.c_double)]

    def __init__(self):
        self.nr_steps = 1000
        self.dt = 0.00001
        self.t0 = 0.
        self.x0 = 2./3
        self.t = (ctypes.c_double * self.nr_steps)()
        self.x = (ctypes.c_double * self.nr_steps)()

class SOLVER(object):
    def __init__(self):
        self.clib = ctypes.CDLL('rk4.dll')
        self.clib.my_fun.argtypes = [ctypes.POINTER(DATA)]
        self.clib.my_fun.restype = None

    def func(self, data_struc):
        self.clib.my_fun(ctypes.byref(data_struc))

solver = SOLVER()
data = DATA()
solver.func(data)

在Windows 8上使用MinGW与gcc -o -c my_clib.o my_clib.c + gcc -o rk4.dll -shared my_clib.o进行编译。

一切正常,在最后一行solver.func()之后,时间数据和解决方案数据存储在data.tdata.x中。现在我需要从指针访问计算数据。似乎无法直接完成。如果您执行type(data.x),则会获得<class '__main__.LP_c_double'>,但如果您尝试访问type(data.x[i]),则会获得标准double

每次我尝试plot(data.t, data.x)或将其强制转换为np.array(data.t)时,Python文件崩溃并且cmd冻结。但是我认为x_python = [data.x[i] for x i in range(*number_of_elements*)]可以正常工作,但如果数组很长,它会很慢。

我的问题是:访问C-solver中计算的数据的正确/最佳方式是什么?

另外,如果这不是将数组从C传递到Python的最佳方法,那么还有哪些其他选择适合这种应用程序?即对于每个时间步,或者可能在达到某个最后时间之后,将解决方案(t, x)(元组或两个数组)从C传递给Python?

2 个答案:

答案 0 :(得分:0)

要访问行为int,long等的ctypes对象的值,请使用:

x = ctypes.c_int(123)
print x.value

所以,你可以迭代它们并制作你的数组。

另外,你可以将一个清晰的C静态数组传递给Python而不是使用结构,并使用numpy的ctypes支持来获取numpy.ndarray,或者使用Python的数组模块。

但是,我明白了你的观点。这些都是简单而干净的方法。

答案 1 :(得分:0)

您可以使用numpy.ctypeslib.as_array

  

从ctypes数组或ctypes POINTER创建一个numpy数组。 numpy数组与ctypes对象共享内存。

numpy.ctypeslib.as_array(data.x, (data.nr_steps,)

只有一点issue重复使用不同形状的相同指针。