Python - 使用ctypes在C和Python之间传递指针

时间:2017-04-01 22:20:07

标签: python c pointers ctypes

我试图在Python中编写一个小型的模块化程序,它将动态加载C函数并使用它们来执行计算密集型代码。在这个程序中,我创建了几个大型矩阵,我将在Python代码之间来回传递给不同的C函数。我宁愿通过引用传递这些矩阵,以避免额外的计算开销。

我已经尝试通过Python文档阅读ctypes,但它似乎并没有解释如何做到这一点。我理解,例如,我可以使用byref()或pointer()将指针从Python传递给C函数,但是如何将指针从外部C函数传递回Python?鉴于变量是Python中的名称,这是刚刚完成的#34;自动" (当缺少一个更好的术语时)Python从C函数接收一个值?

作为一个具体的例子,这就是我尝试做的事情(用伪代码):

foo = ctypes.CDLL("pathToFoo")
bar = ctypes.CDLL("pathToBar")

# Generate a large matrix by calling a C function.
reallyBigMatrix = foo.generateReallyBigMatrix()

# Pass reallyBigMatrix to another function and perform some operation
# on it. Since the matrix is really big, I would prefer to pass a 
# reference to this matrix to my next C function rather than passing
# the matrix by value.
modifiedReallyBigMatrix = bar.modifyReallyBigMatrix(reallBigMatrix)

或者,我结合使用Python和C,因为我需要一种在程序中动态加载C函数的简单方法。我可以将路径传递给不同的C文件到我的Python程序,以便Python程序将在不同的函数上执行相同的代码。作为一个例子,我可能想以两种不同的方式运行我的程序:保持相同的" generateReallyBigMatrix"两个运行中的函数,但使用了不同的" modifyReallyBigMatrix"运行1和运行2之间的程序。如果有一种简单的跨平台方式在C或C ++中执行此操作,我将很乐意实现该解决方案,而不是使用ctypes和Python。但是,我还没能找到一个简单的跨平台解决方案。

1 个答案:

答案 0 :(得分:2)

您已经提到过您正在编写自己的所有代码,包括Python和C.我建议不要使用ctypes,因为ctypes最适合使用无法修改的C库。

相反,使用Python C API在C中编写模块。它将公开一个单独的函数,如下所示:

PyObject* generateReallyBigMatrix(void);

现在,您可以返回任何您喜欢的Python对象,而不是尝试返回原始C指针。这里的好选择是返回NumPy数组(使用NumPy C API),或返回Python "buffer"(如果需要,可以在Python中构造NumPy数组)。

无论哪种方式,一旦使用适当的API用C语言编写此函数,您的Python代码将很简单:

import foo
reallyBigMatrix = foo.generateReallyBigMatrix()

要使用NumPy C API执行此操作,您的C代码将如下所示:

PyObject* generateReallyBigMatrix(void)
{
    npy_intp dimension = 100;
    PyArray_Descr* descr;
    PyArray_DescrAlignConverter2("float64", &descr); // use any dtype

    PyObject* array = PyArray_Empty(1, &dimension, descr, 0/*fortran*/);
    Py_DECREF(descr);

    void* data = PyArray_DATA(array);
    // TODO: populate data

    return array;
}

static PyMethodDef methods[] = {
    {"generateReallyBigMatrix", generateReallyBigMatrix, METH_VARARGS, "doc"},
    {NULL, NULL, 0, NULL}        /* Sentinel */
};

PyMODINIT_FUNC initfoo(void)
{
    import_array(); // enable NumPy C API
    Py_InitModule("foo", methods);
}

请注意,NumPy C API需要稍微奇怪的initialization ritual。另请参阅Numpy C API: Link several object files

然后将代码编译为名为foo.so的共享库(无lib前缀)。