2024-04-24 12:26:10 发布
网友
Python函数需要传入一个Python对象。因为您希望Python对象是一个NumPy数组,所以应该使用NumPy C-API functions for creating arrays;^{}中的一个可能是一个好的开始。它将使用提供的缓冲区,而不复制数据。
也就是说,用Python编写主程序并为C代码使用C扩展模块几乎总是比较容易的。这种方法使Python更容易进行内存管理,而ctypes模块和Numpy的cpython扩展使向C函数传递Numpy数组变得容易。
ctypes
cpython
实际上,这里最好的答案可能是专门使用numpy数组,即使是从您的C代码中。但如果这不可能,那么您将遇到与在C类型和Python类型之间共享数据的任何代码相同的问题。
numpy
一般来说,在C和Python之间共享数据至少有五个选项:
list
__getitem__
intptr_t
const char *
str
bytes
struct
buffer
在您的例子中,您希望在Python中使用numpy.arrays。因此,一般情况是:
numpy.array
对于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了解有关您的选项的更多详细信息):
array
PyObject *makearray(int array[], size_t size) { npy_int dim = size; return PyArray_SimpleNewFromData(1, &dim, (void *)array); }
无论如何,不管你怎么做,你最终都会得到一个类似于C的PyObject *的结果(并且只有一个refcount),所以你可以把它作为函数参数传递,而在Python端,它看起来像是numpy.array,list,bytes,或者其他合适的东西。
PyObject *
现在,如何传递函数参数?好吧,您在注释中引用的Pure Embedding中的示例代码演示了如何做到这一点,但并没有真正解释发生了什么。实际上,扩展文档中的解释比嵌入文档中的解释更多,特别是Calling Python Functions from C。另外,请记住standard library source code中充满了这方面的示例(尽管其中一些示例的可读性不如预期的好,可能是因为优化,或者只是因为它们没有被更新以利用新的简化C API特性)。
跳过第一个从Python获取Python函数的例子,因为您可能已经拥有了它。第二个例子(以及关于它的段落)展示了简单的方法:用^{}创建一个参数元组。所以,假设我们要调用一个存储在myfunc中的函数,它的列表由上面的makelist函数返回。你要做的是:
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
Py_BuildValue
[PyObject_CallObject][6]
PyObject_CallObject(callable_object, args)
apply(callable_object, args)
callable_object(*args)
myfunc(mylist)
myfunc(*(mylist,))
PyObject *arglist = PyTuple_Pack(1, mylist);
但是通常,Py_BuildValue更容易(特别是如果您还没有将所有内容打包为Python对象),并且代码中的意图也更清晰(就像usingPyArg_ParseTuple比在另一个方向使用显式tuple函数更简单和清晰。
PyArg_ParseTuple
tuple
那么,你怎么得到这个myfunc?好吧,如果你已经从嵌入代码中创建了函数,只要保持指针在周围就行了。如果您希望它从Python代码传入,这正是第一个示例所做的。如果您想,例如,从模块或其他上下文中按名称查找它,具体类型(如^{})和抽象类型(如^{})的api非常简单,而且通常很明显,如何将Python代码转换为等效的C代码,即使结果大多是难看的样板文件。
把它们放在一起,假设我有一个C整数数组,我想import mymodule并调用一个返回int的函数mymodule.myfunc(mylist)。下面是一个精简的示例(没有实际测试,也没有错误处理,但它应该显示所有部分):
import mymodule
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-Cython、boost::python等而设计的熟悉框架。即使您正在嵌入,您实际上也在做与扩展相同的工作,因此它们可以以相同的方式提供帮助。
因此,如果您在文档周围搜索,其中一些也具有帮助嵌入部分的工具。例如,您可以使用Cython编写主程序,同时使用C代码和Python代码以及cython --embed。你可能想交叉手指和/或牺牲一些小鸡,但如果它有效,它是惊人的简单和富有成效。Boost在开始的时候并不是那么简单,但是一旦你把事情安排好了,几乎所有的事情都按照你期望的方式完成了,并且工作正常,这对于embedding和扩展来说都是一样的。等等。
cython --embed
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之间共享数据至少有五个选项:
list
或其他要传递的对象。__getitem__
,等等)。intptr_t
,或显式ctypes
类型,或者不强制转换;然后在Python端使用ctypes
来访问它。const char *
,并将其作为str
(或者,在Py3中,bytes
)传递,并在Python端使用struct
或ctypes
来访问它。buffer
协议匹配的对象,然后在Python端再次使用struct
或ctypes
。在您的例子中,您希望在Python中使用
numpy.array
s。因此,一般情况是:numpy.array
。ctypes
将其转换为numpy
可以转换为数组的类型。const char *
,并将其作为str
(或者,在Py3中,bytes
)传递,该类型已经是numpy
可以转换为数组的类型。buffer
协议的对象,我再次相信numpy
可以直接转换。对于1,这里是如何使用
list
来实现它的,因为这是一个非常简单的示例(我已经编写了它……):这里是
numpy.array
等价物(假设您可以依赖于不被删除的Carray
请参见文档中的Creating arrays了解有关您的选项的更多详细信息):无论如何,不管你怎么做,你最终都会得到一个类似于C的
PyObject *
的结果(并且只有一个refcount),所以你可以把它作为函数参数传递,而在Python端,它看起来像是numpy.array
,list
,bytes
,或者其他合适的东西。现在,如何传递函数参数?好吧,您在注释中引用的Pure Embedding中的示例代码演示了如何做到这一点,但并没有真正解释发生了什么。实际上,扩展文档中的解释比嵌入文档中的解释更多,特别是Calling Python Functions from C。另外,请记住standard library source code中充满了这方面的示例(尽管其中一些示例的可读性不如预期的好,可能是因为优化,或者只是因为它们没有被更新以利用新的简化C API特性)。
跳过第一个从Python获取Python函数的例子,因为您可能已经拥有了它。第二个例子(以及关于它的段落)展示了简单的方法:用^{} 创建一个参数元组。所以,假设我们要调用一个存储在
myfunc
中的函数,它的列表由上面的makelist
函数返回。你要做的是:当然,如果您确定有一个有效的可调用对象,则可以跳过可调用检查。(如果合适的话,通常最好在第一次得到
myfunc
时进行检查,因为这样可以提供更早更好的错误反馈。)如果您想真正了解发生了什么,请尝试不使用
Py_BuildValue
。正如文档所说,[PyObject_CallObject][6]
的第二个参数是一个元组,PyObject_CallObject(callable_object, args)
相当于apply(callable_object, args)
,相当于callable_object(*args)
。所以,如果你想在Python中调用myfunc(mylist)
,你必须把它有效地转换成myfunc(*(mylist,))
,这样你就可以把它转换成C但是通常,
Py_BuildValue
更容易(特别是如果您还没有将所有内容打包为Python对象),并且代码中的意图也更清晰(就像usingPyArg_ParseTuple
比在另一个方向使用显式tuple
函数更简单和清晰。那么,你怎么得到这个} )和抽象类型(如^{} )的api非常简单,而且通常很明显,如何将Python代码转换为等效的C代码,即使结果大多是难看的样板文件。
myfunc
?好吧,如果你已经从嵌入代码中创建了函数,只要保持指针在周围就行了。如果您希望它从Python代码传入,这正是第一个示例所做的。如果您想,例如,从模块或其他上下文中按名称查找它,具体类型(如^{把它们放在一起,假设我有一个C整数数组,我想
import mymodule
并调用一个返回int的函数mymodule.myfunc(mylist)
。下面是一个精简的示例(没有实际测试,也没有错误处理,但它应该显示所有部分):如果你使用C++,你可能想查看某种范围的守护程序/看门人等来处理所有这些{{CD53}}调用,特别是在你开始正确的错误处理(通常意味着早期^ {< CD54 >}通过函数调用的时候)。如果你使用C++ 11或Boost,^ {CD55>}可能是你所需要的。
但实际上,如果您计划进行大量的C<;->;Python通信,那么最好的方法是查看所有为改进扩展Python-Cython、boost::python等而设计的熟悉框架。即使您正在嵌入,您实际上也在做与扩展相同的工作,因此它们可以以相同的方式提供帮助。
因此,如果您在文档周围搜索,其中一些也具有帮助嵌入部分的工具。例如,您可以使用Cython编写主程序,同时使用C代码和Python代码以及
cython --embed
。你可能想交叉手指和/或牺牲一些小鸡,但如果它有效,它是惊人的简单和富有成效。Boost在开始的时候并不是那么简单,但是一旦你把事情安排好了,几乎所有的事情都按照你期望的方式完成了,并且工作正常,这对于embedding和扩展来说都是一样的。等等。相关问题 更多 >
编程相关推荐