使用ctypes从Python调用C:传递向量

4
我希望使用ctypes从python调用C函数。从文档中,我不明白如何传递指向向量的指针。我要调用的函数是:
double f(int n, double* x)
  {
    int i;
    double p=1;
    for (i=0; i< n; i ++) 
      p = p * x[i];
    return p;
  }

我已经使用了空指针修改了这个函数,所以它变成了f(int, void*),并且进行了内部转换为double类型。我执行以下操作:

def f(x):
  n = len(x)
  libc = '/path/to/lib.so'
  cn = c_int(n)
  px = pointer(x)
  cx = c_void_p(px)
  libc.restype = c_double
  L = libc.f(cn, cx)
  return L

我假设x是一个numpy数组,但我不确定numpy数组在内存中的组织方式是否是最佳解决方案。
编辑:
所有提出的方法都不能处理我的numpy数组,可能是由于我定义数组的方式不同。
x = np.array([], 'float64')
f = open(file,'r')
for line in f:
  x = np.append(x,float(line))

但是有些函数只能接受显式列表,比如[1,2,3,4,5],而不能接受已经在其他地方定义并被引用为x的列表。


1
请看这个问题:https://dev59.com/Y2025IYBdhLWcg3wr4B6 - ThePhysicist
3个回答

4

基于@Sven Marnach的回答

#!/usr/bin/env python
import ctypes
import numpy as np
from numpy.ctypeslib import ndpointer

libf = ctypes.cdll.LoadLibrary('/path/to/lib.so')
libf.f.restype = ctypes.c_double
libf.f.argtypes = [ctypes.c_int, ndpointer(ctypes.c_double)]

def f(a):
    return libf.f(a.size, np.ascontiguousarray(a, np.float64))

if __name__=="__main__":
    # slice to create non-contiguous array
    a = np.arange(1, 7, dtype=np.float64)[::2]
    assert not a.flags['C_CONTIGUOUS']
    print(a)
    print(np.multiply.reduce(a))
    print(f(a))

输出

[ 1.  3.  5.]
15.0
15.0

移除np.ascontiguousarray()函数调用会产生错误的结果(在我的机器上是6.0)。


2
您可以这样调用它:
#!python
from ctypes import *

#!python
from ctypes import *

# double f(int n, double* x)
f = CDLL('/path/to/lib.so').f
f.argtypes = [c_int, POINTER(c_double)]
f.restype = c_double

if __name__ == '__main__':
    array = (c_double * 5)(1, 2, 3, 4, 5)
    r = f(len(array), array)
    print(r)

如果您有numpy数组,您可以使用numpy.array.ctypes.data_as
#!python
from ctypes import *
import numpy

# double f(int n, double* x)
f = CDLL('/path/to/lib.so').f
f.argtypes = [c_int, POINTER(c_double)]
f.restype = c_double

if __name__ == '__main__':
    array = numpy.array([1, 2, 3, 4, 5])
    r = f(array.size, array.astype(numpy.double).ctypes.data_as(POINTER(c_double)))
    print(r)

或者:

#!python
from ctypes import *
import numpy

# double f(int n, double* x)
f = CDLL('/path/to/lib.so').f
f.argtypes = [c_int, POINTER(c_double)]
f.restype = c_double

if __name__ == '__main__':
    array = numpy.double([1, 2, 3, 4, 5])
    r = f(array.size, array.ctypes.data_as(POINTER(c_double)))
    print(r)

1
或者 f.argtypes = [c_int, numpy.ctypeslib.ndpointer('float64')]; r = f(array.size, array) - Eryk Sun
@eryksun 我收到了“参数错误:‘参数2:<type 'exceptions.TypeError'>:不知道如何转换参数2’” - simona
@BSH 我目前只尝试了第二种方法,但数值传递不正确。 - simona
@simona,我不确定你所说的“未正确传递”的意思是什么,你是如何传递它的? - user1129665
@simona 确保你传递正确的大小和类型,5 是硬编码的大小。对我来说它很好用。 - user1129665
显示剩余5条评论

-2

1
在Stack Overflow上,只提供链接的答案通常不受欢迎。随着时间的推移,链接可能会失效并变得无法访问,这意味着你的答案对未来的用户毫无用处。最好在实际帖子中提供您回答的一般细节,并引用您的链接作为参考。 - jfs
@eryksun 看,我不想争论什么,只是我按照教程的指示操作,现在我的代码可以运行了。 - simona
@J.F.Sebastian,现在我已经更新了我的答案,只是因为你是对的,这可能对未来有相同问题的其他用户有帮助。 我得到了一个-1,尽管我的答案是正确的并解决了我的问题,但有时我觉得stackoverflow社区的人们很无助,真的。 - simona
如果您尝试将我的答案中的非连续数组传递给libc.f,那么会导致ctypes.ArgumentError: argument 2: <class 'TypeError'>: array must have flags ['C_CONTIGUOUS']。您认为这个方法可行吗?注意:我的答案中的f()函数生成乘积。无关的话题:我没有对答案进行投票。虽然我应该这样做,因为答案是错误的(TypeError不是正确的答案),而且答案的作者拒绝更改它。 - jfs
1
@simona:发生的情况是人们在试图修复某些东西时对代码进行了许多更改,并错误地将修复归因于根本没有起作用的东西。这一定是在这里的情况。很抱歉你不相信我。我希望通过查看源代码,您会发现它像1 + 1 = 2一样明显。我只是想帮助你。 - Eryk Sun
显示剩余6条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接