Python ctypes 和 np.array.ctypes.data_as 索引时的意外行为

问题描述 投票:0回答:1

使用 Python ctypes 和 numpy 库,我将数据传递到共享库并遇到非常奇怪的行为

C 函数:

#include <stdio.h>
typedef struct {
    double *a;
    double *b;
} s_gate;
void printdouble(s_gate*, int);

void printdouble(s_gate *gate, int n) {
    for (int i =0; i < n; i++) {
        printf("gate->a[%d] = %f\n", i, gate->a[i]);
    }
    for (int i =0; i < n; i++) {
        printf("gate->b[%d] = %f\n", i, gate->b[i]);
    }
}

Python代码:

import ctypes
import numpy as np
class s_gate(ctypes.Structure):
    _fields_ = [('a', ctypes.POINTER(ctypes.c_double)),
                ('b', ctypes.POINTER(ctypes.c_double))]

    def __init__(self, mydict:dict):
        mask = [True, False, True, True, True, True, False, False, False, True]
        a = np.ascontiguousarray(mydict['a'], dtype=np.double)
        b = np.ascontiguousarray(mydict['b'], dtype=np.double)
        setattr(self, 'a', a[0,:].ctypes.data_as(ctypes.POINTER(ctypes.c_double)))
        setattr(self, 'b', b[0,:].ctypes.data_as(ctypes.POINTER(ctypes.c_double)))
        self.size = 10 

if __name__ == "__main__":

    a = np.array([[1,2,3,4,5,6,7,8,9,10], [10,9,8,7,6,5,4,3,2,1]], dtype=np.double).T
    b = a + 100
    
    data = {'a': a, 'b': b}

    mylib = ctypes.CDLL('./mwe.so')
    mylib.printdouble.argstype = [ctypes.POINTER(s_gate), ctypes.c_int]
    mylib.printdouble.restype = ctypes.c_void_p
    print(f'Sending \n{a} and \n{b}')
    gate = s_gate(data)

    mylib.printdouble(ctypes.byref(gate), gate.size)

运行这段代码,我得到了预期的结果,即:

[[ 1.  2.  3.  4.  5.  6.  7.  8.  9. 10.]
 [10.  9.  8.  7.  6.  5.  4.  3.  2.  1.]] and 
[[101. 102. 103. 104. 105. 106. 107. 108. 109. 110.]
 [110. 109. 108. 107. 106. 105. 104. 103. 102. 101.]]
gate->a[0] = 1.000000
gate->a[1] = 2.000000
gate->a[2] = 3.000000
gate->a[3] = 4.000000
gate->a[4] = 5.000000
gate->a[5] = 6.000000
...
gate->b[0] = 101.000000
gate->b[1] = 102.000000
gate->b[2] = 103.000000
gate->b[3] = 104.000000
gate->b[4] = 105.000000
gate->b[5] = 106.000000
...

现在,让我们在

mask
类的
__init__
方法中使用
s_gate
变量。因此,我们将第 11 行和第 12 行替换为 :

setattr(self, 'a', a[0,mask].ctypes.data_as(ctypes.POINTER(ctypes.c_double)))
setattr(self, 'b', b[0,mask].ctypes.data_as(ctypes.POINTER(ctypes.c_double)))
self.size = sum(mask)

现在结果是:

gate->a[0] = 0.000000
gate->a[1] = 0.000000
gate->a[2] = 0.000000
gate->a[3] = 0.000000
gate->a[4] = 0.000000
gate->a[5] = 0.000000
gate->b[0] = 101.000000
gate->b[1] = 103.000000
gate->b[2] = 104.000000
gate->b[3] = 105.000000
gate->b[4] = 106.000000
gate->b[5] = 110.000000

所有gate.a数据归零!对于gate.a来说,预期结果当然是[1,3,4,5,6,10]

到目前为止我尝试过的:

使用

np.require
np.ascontiguousarray
保证C数组连续,执行整数索引而不是逻辑索引,执行2D逻辑索引(
array[slices_xy]
而不是
array[slice_x, slice_y]
)。
使用各种复制、深复制方法并通过第一次切片创建中间数据...

没有任何效果,一旦出现此代码中描述的切片(而不是

mask
),就会出现此行为。出了什么问题?
    

python numpy ctypes
1个回答
0
投票
:

对象中保存对它们的引用,然后它就可以工作了:

s_gate

输出:

import ctypes as ct import numpy as np PDOUBLE = ct.POINTER(ct.c_double) class s_gate(ct.Structure): _fields_ = [('a', PDOUBLE), ('b', PDOUBLE)] def __init__(self, a, b): mask = [True, False, True, True, True, True, False, False, False, True] self.tmpa = a[0,mask] # hold a reference to the arrays self.tmpb = b[0,mask] self.a = self.tmpa.ctypes.data_as(PDOUBLE) # get pointers to the arrays self.b = self.tmpb.ctypes.data_as(PDOUBLE) self.size = sum(mask) a = np.array([[1,2,3,4,5,6,7,8,9,10], [10,9,8,7,6,5,4,3,2,1]], dtype=np.double) b = a + 100 mylib = ct.CDLL('./test') mylib.printdouble.argstype = ct.POINTER(s_gate), ct.c_int mylib.printdouble.restype = ct.c_void_p print(f'Sending \n{a} and \n{b}') gate = s_gate(a, b) mylib.printdouble(ct.byref(gate), gate.size)

	
© www.soinside.com 2019 - 2024. All rights reserved.