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

use*_*948 10 python embedding python-c-api

我遇到了一些问题,希望得到一些帮助.我有一个片段代码,用于嵌入python脚本.这个python脚本包含一个函数,它希望接收一个数组作为参数(在这种情况下,我在python脚本中使用numpy数组).我想知道如何将数组从C传递给嵌入式python脚本作为脚本中函数的参数.更具体地说,有人可以向我展示一个简单的例子.

aba*_*ert 18

真的,这里最好的答案可能就是使用numpy数组,即使是从你的C代码.但是,如果这不可能,那么您遇到的问题与在C类型和Python类型之间共享数据的任何代码相同.

通常,在C和Python之间共享数据至少有五个选项:

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

在您的情况下,您希望numpy.array在Python中使用s.因此,一般情况变为:

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

对于1,这里是如何使用a 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;
}
Run Code Online (Sandbox Code Playgroud)

这是numpy.array等效的(假设您可以依赖C array不被删除 - 请参阅在文档中创建数组,以获取有关您的选项的更多详细信息):

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

无论如何,无论如何,你最终会得到一些看起来像是PyObject *来自C(并且有一个引用计数)的东西,所以你可以将它作为函数参数传递,而在Python端它看起来像numpy.array,list,bytes或其他任何合适的东西.

现在,你如何实际传递函数参数?好吧,您在评论中引用的Pure Embedding中的示例代码显示了如何执行此操作,但并未真正解释正在发生的事情.实际上,扩展文档中的解释比嵌入文档更多,特别是从C调用Python函数.另外,请记住,标准库源代码充满了这样的示例(尽管其中一些不是可读的,或者是因为优化,或者仅仅是因为它们尚未更新以利用新的简化C API特性).

跳过关于从Python获取Python函数的第一个例子,因为大概你已经有了.第二个例子(以及关于它的段落)显示了这样做的简单方法:使用创建参数元组Py_BuildValue.所以,假设我们想要调用一个你已经存储的函数和上面那个函数返回myfunc的列表.这是你做的:mylistmakelist

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;
Run Code Online (Sandbox Code Playgroud)

当然,如果你确定你有一个有效的可调用对象,你可以跳过可调用的检查.(通常情况下,最好先检查一下myfunc,如果合适的话,因为你可以通过这种方式提供更早和更好的错误反馈.)

如果你想真正理解发生了什么,那就试试吧Py_BuildValue.正如文档所说,第二个参数[PyObject_CallObject][6]是一个元组,PyObject_CallObject(callable_object, args)相当于apply(callable_object, args),相当于callable_object(*args).所以,如果你想myfunc(mylist)用Python 调用,你必须有效地将其转换为,myfunc(*(mylist,))所以你可以将它转换为C.你可以构造tuple如下:

PyObject *arglist = PyTuple_Pack(1, mylist);
Run Code Online (Sandbox Code Playgroud)

但通常,Py_BuildValue更容易(特别是如果你还没有把所有内容打包成Python对象),并且代码中的意图更清晰(就像使用PyArg_ParseTupletuple在另一个方向上使用显式函数更简单和清晰).

那么,你怎么做到的myfunc?好吧,如果你已经从嵌入代码创建了函数,只需保持指针.如果你想从Python代码传入它,那正是第一个例子所做的.如果你想,例如,从模块或其他上下文中查找名称,具体类型的API PyModule和类似的抽象类型PyMapping非常简单,并且通常很明显如何将Python代码转换为等效的C代码,即使结果大多是丑陋的样板.

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

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;
}
Run Code Online (Sandbox Code Playgroud)

如果您正在使用C++,您可能想要查看某种范围内的看门人/看门人/等.处理所有这些Py_DECREF调用,特别是一旦你开始进行适当的错误处理(这通常意味着return NULL通过函数填充的早期调用).如果你正在使用C++ 11或Boost,unique_ptr<PyObject, Py_DecRef>可能就是你所需要的.

但实际上,如果你计划进行大量的C < - > Python通信,那么减少所有丑陋样板的更好方法是查看所有为改进扩展Python- Cython而设计的熟悉框架,boost :: python,即使你正在嵌入,你也可以有效地完成与扩展相同的工作,因此他们可以以同样的方式提供帮助.

就此而言,如果您搜索文档,其中一些还有帮助嵌入部分的工具.例如,您可以使用C代码和Python代码在Cython中编写主程序cython --embed.您可能想要交叉手指和/或牺牲一些鸡,但如果它有效,它就会非常简单和富有成效.Boost并不是一件容易上手的事情,但是一旦你把事情放在一起,几乎所有东西都完全按照你期望的方式完成,并且正常工作,这就像嵌入式扩展一样真实.等等.