如何将数组从C传递到嵌入式python脚本

2024-04-24 12:26:10 发布

您现在位置:Python中文网/ 问答频道 /正文


Tags: python
2条回答

Python函数需要传入一个Python对象。因为您希望Python对象是一个NumPy数组,所以应该使用NumPy C-API functions for creating arrays^{}中的一个可能是一个好的开始。它将使用提供的缓冲区,而不复制数据。

也就是说,用Python编写主程序并为C代码使用C扩展模块几乎总是比较容易的。这种方法使Python更容易进行内存管理,而ctypes模块和Numpy的cpython扩展使向C函数传递Numpy数组变得容易。

实际上,这里最好的答案可能是专门使用numpy数组,即使是从您的C代码中。但如果这不可能,那么您将遇到与在C类型和Python类型之间共享数据的任何代码相同的问题。

一般来说,在C和Python之间共享数据至少有五个选项:

  1. 创建一个Pythonlist或其他要传递的对象。
  2. 定义一个新的Python类型(在C代码中)来包装和表示数组,方法与在Python中为序列对象定义的方法相同(__getitem__,等等)。
  3. 将指向数组的指针强制转换为intptr_t,或显式ctypes类型,或者不强制转换;然后在Python端使用ctypes来访问它。
  4. 将指向数组的指针投射到const char *,并将其作为str(或者,在Py3中,bytes)传递,并在Python端使用structctypes来访问它。
  5. 创建与buffer协议匹配的对象,然后在Python端再次使用structctypes

在您的例子中,您希望在Python中使用numpy.arrays。因此,一般情况是:

  1. 创建一个要传递的numpy.array
  2. (可能不合适)
  3. 按原样将指针传递给数组,然后从Python中,使用ctypes将其转换为numpy可以转换为数组的类型。
  4. 将指向数组的指针转换为const char *,并将其作为str(或者,在Py3中,bytes)传递,该类型已经是numpy可以转换为数组的类型。
  5. 创建一个匹配buffer协议的对象,我再次相信numpy可以直接转换。

对于1,这里是如何使用list来实现它的,因为这是一个非常简单的示例(我已经编写了它……):

PyObject *makelist(int array[], size_t size) {
    PyObject *l = PyList_New(size);
    for (size_t i = 0; i != size; ++i) {
        PyList_SET_ITEM(l, i, PyInt_FromLong(array[i]));
    }
    return l;
}

这里是numpy.array等价物(假设您可以依赖于不被删除的C array请参见文档中的Creating arrays了解有关您的选项的更多详细信息):

PyObject *makearray(int array[], size_t size) {
    npy_int dim = size;
    return PyArray_SimpleNewFromData(1, &dim, (void *)array);
}

无论如何,不管你怎么做,你最终都会得到一个类似于C的PyObject *的结果(并且只有一个refcount),所以你可以把它作为函数参数传递,而在Python端,它看起来像是numpy.arraylistbytes,或者其他合适的东西。

现在,如何传递函数参数?好吧,您在注释中引用的Pure Embedding中的示例代码演示了如何做到这一点,但并没有真正解释发生了什么。实际上,扩展文档中的解释比嵌入文档中的解释更多,特别是Calling Python Functions from C。另外,请记住standard library source code中充满了这方面的示例(尽管其中一些示例的可读性不如预期的好,可能是因为优化,或者只是因为它们没有被更新以利用新的简化C API特性)。

跳过第一个从Python获取Python函数的例子,因为您可能已经拥有了它。第二个例子(以及关于它的段落)展示了简单的方法:用^{}创建一个参数元组。所以,假设我们要调用一个存储在myfunc中的函数,它的列表由上面的makelist函数返回。你要做的是:

if (!PyCallable_Check(myfunc)) {
    PyErr_SetString(PyExc_TypeError, "function is not callable?!");
    return NULL;
}
PyObject *arglist = Py_BuildValue("(o)", mylist);
PyObject *result = PyObject_CallObject(myfunc, arglist);
Py_DECREF(arglist);
return result;

当然,如果您确定有一个有效的可调用对象,则可以跳过可调用检查。(如果合适的话,通常最好在第一次得到myfunc时进行检查,因为这样可以提供更早更好的错误反馈。)

如果您想真正了解发生了什么,请尝试不使用Py_BuildValue。正如文档所说,[PyObject_CallObject][6]的第二个参数是一个元组,PyObject_CallObject(callable_object, args)相当于apply(callable_object, args),相当于callable_object(*args)。所以,如果你想在Python中调用myfunc(mylist),你必须把它有效地转换成myfunc(*(mylist,)),这样你就可以把它转换成C

PyObject *arglist = PyTuple_Pack(1, mylist);

但是通常,Py_BuildValue更容易(特别是如果您还没有将所有内容打包为Python对象),并且代码中的意图也更清晰(就像usingPyArg_ParseTuple比在另一个方向使用显式tuple函数更简单和清晰。

那么,你怎么得到这个myfunc?好吧,如果你已经从嵌入代码中创建了函数,只要保持指针在周围就行了。如果您希望它从Python代码传入,这正是第一个示例所做的。如果您想,例如,从模块或其他上下文中按名称查找它,具体类型(如^{})和抽象类型(如^{})的api非常简单,而且通常很明显,如何将Python代码转换为等效的C代码,即使结果大多是难看的样板文件。

把它们放在一起,假设我有一个C整数数组,我想import mymodule并调用一个返回int的函数mymodule.myfunc(mylist)。下面是一个精简的示例(没有实际测试,也没有错误处理,但它应该显示所有部分):

int callModuleFunc(int array[], size_t size) {
    PyObject *mymodule = PyImport_ImportModule("mymodule");
    PyObject *myfunc = PyObject_GetAttrString(mymodule, "myfunc");
    PyObject *mylist = PyList_New(size);
    for (size_t i = 0; i != size; ++i) {
        PyList_SET_ITEM(l, i, PyInt_FromLong(array[i]));
    }
    PyObject *arglist = Py_BuildValue("(o)", mylist);
    PyObject *result = PyObject_CallObject(myfunc, arglist);
    int retval = (int)PyInt_AsLong(result);
    Py_DECREF(result);
    Py_DECREF(arglist);
    Py_DECREF(mylist);
    Py_DECREF(myfunc);
    Py_DECREF(mymodule);
    return retval;
}

如果你使用C++,你可能想查看某种范围的守护程序/看门人等来处理所有这些{{CD53}}调用,特别是在你开始正确的错误处理(通常意味着早期^ {< CD54 >}通过函数调用的时候)。如果你使用C++ 11或Boost,^ {CD55>}可能是你所需要的。

但实际上,如果您计划进行大量的C<;->;Python通信,那么最好的方法是查看所有为改进扩展Python-Cythonboost::python等而设计的熟悉框架。即使您正在嵌入,您实际上也在做与扩展相同的工作,因此它们可以以相同的方式提供帮助。

因此,如果您在文档周围搜索,其中一些具有帮助嵌入部分的工具。例如,您可以使用Cython编写主程序,同时使用C代码和Python代码以及cython --embed。你可能想交叉手指和/或牺牲一些小鸡,但如果它有效,它是惊人的简单和富有成效。Boost在开始的时候并不是那么简单,但是一旦你把事情安排好了,几乎所有的事情都按照你期望的方式完成了,并且工作正常,这对于embedding和扩展来说都是一样的。等等。

相关问题 更多 >