Embedding Python in C/C++

将Python嵌入C/C++

首先嵌入和扩展是相关联但不同的两个概念:嵌入是指在C语言中使用Python,而扩展是在Python中使用C语言(以library的形式)。

其次嵌入Python的本质是嵌入Python 解释器(Interpretor)。因此我们需要调用相应的Initialize, Finalize函数。另外,为了让C/C++识别Python相关的函数,我们还需要#include 以及相应的linker options:-Xlinker -export-dynamic -ldl -lrt -lutil

另外,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++