将Python嵌入C/C++
首先嵌入和扩展是相关联但不同的两个概念:嵌入是指在C语言中使用Python,而扩展是在Python中使用C语言(以library的形式)。
其次嵌入Python的本质是嵌入Python 解释器(Interpretor)。因此我们需要调用相应的Initialize, Finalize函数。另外,为了让C/C++识别Python相关的函数,我们还需要#include
另外,Python语句有两种:statement 和 expression。注意statement是没有返回值的。因此Python语言里有exec和eval分别对应这两种情形。最本质的区别是statement有副作用(side effect),比如会把值绑定到一个名称上,比如: a = 3 。当我们用PyRun_SimpleString(“a=3”)时,这种副作用是在当前的environment下(内部实现是dict)多出一个变量(dict的key),名称是a。
此外要注意Python的执行代码是和environment相关的,比如global和local,想用的函数名称(例如:dir,str,print),变量都是保存在各自的environment里。平时我们写的 if name == "__main__"
就是说默认的环境是在模块__main__里面。我们要取出默认的函数或者变量(Python内部不严格区分这两个概念,知识函数可以callable),可以用下面的代码(以取出dir函数为例):
PyObject* main_module = PyImport_AddModule("__main__"); // Get the main module's dictionary // and make a copy of it. PyObject* main_dict = PyModule_GetDict(main_module); // pFunc is also a borrowed reference PyObject* pFunc = PyDict_GetItemString(pDict, "dir");
嵌入Python还应注意内存的使用。因为Python主要使用(另一种是Python的malloc, free)Reference count方式来管理新的变量,我们应记住在获得一个PyObject*类型的指针之后,用Py_DECREF或者Py_XDECREF来减少reference count (注意,特殊情况下取出的结果是不能减少reference count的,比如取出list中某个元素)。
最后给一个例子来说明怎么在Python里计算任何表达式expression (参考了FAQ[1])
#include <Python.h> double checkExpression(const char* formular, double gq, double dp) { Py_Initialize(); // Get a reference to the main module. PyObject* main_module = PyImport_AddModule("__main__"); // Get the main module's dictionary // and make a copy of it. PyObject* main_dict = PyModule_GetDict(main_module); char s[1024]; sprintf(s, "GQ=%lf", gq); //, dp, formular); if ( 0 != PyRun_SimpleString(s) ) { // something wrong happen! fprintf(stderr, "\nSomething wrong in assigning GQ\n"); return -1.; } sprintf(s, "DP=%lf", dp); if ( 0 != PyRun_SimpleString(s) ) { // something wrong happen! fprintf(stderr, "\nSomething wrong in assigning DP\n"); return -1.; } PyObject* ret = PyRun_String(formular, Py_eval_input, main_dict, main_dict); if (ret == NULL) { Py_XDECREF(ret); PyErr_Clear(); return -1.; }; double res; if (PyInt_Check(ret)) { res = PyLong_AsLong(ret); } else if (PyFloat_Check(ret)) { res = PyFloat_AS_DOUBLE(ret); } else if (PyBool_Check(ret)) { res = ret == Py_True; } Py_XDECREF(ret); Py_Finalize(); return res; };
重要参考资料
【1】 扩展/嵌入Python的FAQ Extending/Embedding FAQ
【2】API 手册 Python/C API Reference Manual
【3】嵌入Python的流程性说明 Embedding Python in Another Application
【4】扩展Python的流程性说明,这里介绍了Python底层的知识,这些知识不会在”嵌入Python的流程性说明”中重复出现 Extending Python with C or C++