你想在 C 中安全的执行某个 Python 调用并返回结果给 C。 例如,你想在 C 语言中使用某个 Python 函数作为一个回调。
解决方案
在 C 语言中调用 Python 非常简单,不过涉及到一些小窍门。 下面的 C 代码告诉你怎样安全的调用:
#include <Python.h>/* Execute func(x,y) in the Python interpreter. The arguments and return result of the function must be Python floats */doublecall_func(PyObject*func,doublex,doubley){ PyObject *args; PyObject *kwargs; PyObject *result =0;double retval;/* Make sure we own the GIL */ PyGILState_STATE state =PyGILState_Ensure();/* Verify that func is a proper callable */if(!PyCallable_Check(func)){fprintf(stderr,"call_func: expected a callable\n");gotofail;}/* Build arguments */ args =Py_BuildValue("(dd)", x, y); kwargs =NULL;/* Call the function */ result =PyObject_Call(func, args, kwargs);Py_DECREF(args);Py_XDECREF(kwargs);/* Check for Python exceptions (if any) */if(PyErr_Occurred()){PyErr_Print();gotofail;}/* Verify the result is a float object */if(!PyFloat_Check(result)){fprintf(stderr,"call_func: callable didn't return a float\n");gotofail;}/* Create the return value */ retval =PyFloat_AsDouble(result);Py_DECREF(result);/* Restore previous GIL state and return */PyGILState_Release(state);return retval;fail:Py_XDECREF(result);PyGILState_Release(state);abort();// Change to something more appropriate}
要使用这个函数,你需要获取传递过来的某个已存在 Python 调用的引用。 有很多种方法可以让你这样做, 比如将一个可调用对象传给一个扩展模块或直接写 C 代码从已存在模块中提取出来。
下面是一个简单例子用来演示从一个嵌入的 Python 解释器中调用一个函数:
要构建例子代码,你需要编译 C 并将它链接到 Python 解释器。 下面的 Makefile 可以教你怎样做(不过在你机器上面需要一些配置)。
在 C 代码里处理错误你需要格外的小心。一般来讲,你不能仅仅抛出一个 Python 异常。 错误应该使用 C 代码方式来被处理。在这里,我们打算将对错误的控制传给一个叫 abort() 的错误处理器。 它会结束掉整个程序,在真实环境下面你应该要处理的更加优雅些(返回一个状态码)。 你要记住的是在这里 C 是主角,因此并没有跟抛出异常相对应的操作。 错误处理是你在编程时必须要考虑的事情。
double call_func(PyObject *func, double x, double y) {
// ...
double retval;
/* Make sure we own the GIL */
PyGILState_STATE state = PyGILState_Ensure();
// ...
/* Code that uses Python C API functions */
// ...
/* Restore previous GIL state and return */
PyGILState_Release(state);
return retval;
fail:
PyGILState_Release(state);
abort();
}