1、C语言与 Python交互大夏天编辑整理于互联网,2017Pyton和 C分别有着各自的优缺点,用 Python开发程序速度快,可靠性高,并且有许多高级模块可供使用,但执行速度相对较慢;C 语言则正好相反,执行速度快,但开发效率低。为了利用两种语言各自的优点,比较好的做法是用 Python开发整个软件框架,而用 C语言实现高效功能模块。本文介绍 C与 Python互相调用与结合的方法, 第二部分介绍常用 Python接口,第三部分介绍 Python调用 C程序,第四部分介绍 C调用 Python程序 一、简介 Python是一门功能强大的高级脚本语言,它的强大不仅表现在其自身的功能上,而且还表
2、现在其良好的可扩展性上,正因如此,Python 已经开始受到越来越多人的青睐,并且被屡屡成功地应用于各类大型软件系统的开发过程中。 与其它普通脚本语言有所不同,Python 程序员可以借助 Python语言提供的 API,使用 C或者 C+来对 Python进行功能性扩展,从而即可以利用 Python方便灵活的语法和功能,又可以获得与 C或者 C+几乎相同的执行性能。执行速度慢是几乎所有脚本语言都具有的共性,也是倍受人们指责的一个重要因素,Python 则通过与 C语言的有机结合巧妙地解决了这一问题,从而使脚本语言的应用范围得到了很大扩展。 在用 Python开发实际软件系统时,很多时候都需要
3、使用 C/C+来对 Python进行扩展。最常见的情况是目前已经存在一个用 C编写的库,需要在 Python语言中使用该库的某些功能,此时就可以借助 Python提供的扩展功能来实现。此外,由于 Python从本质上讲还是一种脚本语言,某些功能用 Python实现可能很难满足实际软件系统对执行效率的要求,此时也可以借助 Python提供的扩展功能,将这些关键代码段用 C或者 C+实现,从而提供程序的执行性能。 本文主要介绍 Python提供的 C语言扩展接口,以及如何使用这些接口和 C/C+语言来对 Python进行功能性扩展,并辅以具体的实例讲述如何实现 Python的功能扩展。 二、Pyt
4、hon 的 C语言接口 Python是用 C语言实现的一种脚本语言,本身具有优良的开放性和可扩展性,并提供了方便灵活的应用程序接口(API),从而使得 C/C+程序员能够在各个级别上对 Python解释器的功能进行扩展。在使用 C/C+对 Python进行功能扩展之前,必须首先掌握Python解释所提供的 C语言接口。 2.1 Python对象 (PyObject) Python是一门面向对象的脚本语言,所有的对象在 Python解释器中都被表示成PyObject,PyObject 结构包含 Python对象的所有成员指针,并且对 Python对象的类型信息和引用计数进行维护。在进行 Pyth
5、on的扩展编程时,一旦要在 C或者 C+中对Python对象进行处理,就意味着要维护一个 PyObject结构。 在 Python的 C语言扩展接口中,大部分函数都有一个或者多个参数为 PyObject指针类型,并且返回值也大都为 PyObject指针。 2.2 引用计数 为了简化内存管理,Python 通过引用计数机制实现了自动的垃圾回收功能,Python中的每个对象都有一个引用计数,用来计数该对象在不同场所分别被引用了多少次。每当引用一次 Python对象,相应的引用计数就增 1,每当消毁一次 Python对象,则相应的引用就减 1,只有当引用计数为零时,才真正从内存中删除 Python对
6、象。 下面的例子说明了 Python解释器如何利用引用计数来对 Pyhon对象进行管理:例 1:refcount.pyclass refcount:# etc.r1 = refcount() # 引用计数为 1r2 = r1 # 引用计数为 2del(r1) # 引用计数为 1del(r2) # 引用计数为 0,删除对象在 C/C+中处理 Python对象时,对引用计数进行正确的维护是一个关键问题,处理不好将很容易产生内存泄漏。Python 的 C语言接口提供了一些宏来对引用计数进行维护,最常见的是用 Py_INCREF()来增加使 Python对象的引用计数增 1,用 Py_DECREF()
7、来使Python对象的引用计数减 1。 2.3 数据类型 Python定义了六种数据类型:整型、浮点型、字符串、元组、列表和字典,在使用C语言对 Python进行功能扩展时,首先要了解如何在 C和 Python的数据类型间进行转化。2.3.1 整型、浮点型和字符串 在 Python的 C语言扩展中要用到整型、浮点型和字符串这三种数据类型时相对比较简单,只需要知道如何生成和维护它们就可以了。下面的例子给出了如何在 C语言中使用Python的这三种数据类型:例 2:typeifs.c/ build an integerPyObject* pInt = Py_BuildValue(“i“, 2003
8、);assert(PyInt_Check(pInt);int i = PyInt_AsLong(pInt);Py_DECREF(pInt);/ build a floatPyObject* pFloat = Py_BuildValue(“f“, 3.14f);assert(PyFloat_Check(pFloat);float f = PyFloat_AsDouble(pFloat);Py_DECREF(pFloat);/ build a stringPyObject* pString = Py_BuildValue(“s“, “Python“);assert(PyString_Check(p
9、String);int nLen = PyString_Size(pString);char* s = PyString_AsString(pString);Py_DECREF(pString);2.3.2 元组 Python语言中的元组是一个长度固定的数组,当 Python解释器调用 C语言扩展中的方法时,所有非关键字(non-keyword)参数都以元组方式进行传递。下面的例子示范了如何在 C语言中使用 Python的元组类型:例 3:typetuple.c/ create the tuplePyObject* pTuple = PyTuple_New(3);assert(PyTuple_
10、Check(pTuple);assert(PyTuple_Size(pTuple) = 3);/ set the itemPyTuple_SetItem(pTuple, 0, Py_BuildValue(“i“, 2003);PyTuple_SetItem(pTuple, 1, Py_BuildValue(“f“, 3.14f);PyTuple_SetItem(pTuple, 2, Py_BuildValue(“s“, “Python“);/ parse tuple itemsint i;float f;char *s;if (!PyArg_ParseTuple(pTuple, “ifs“,
11、/ cleanupPy_DECREF(pTuple);2.3.3 列表 Python语言中的列表是一个长度可变的数组,列表比元组更为灵活,使用列表可以对其存储的 Python对象进行随机访问。下面的例子示范了如何在 C语言中使用 Python的列表类型:例 4:typelist.c/create the listPyObject* pList = PyList_New(3); /new referenceassert(PyList_Check(pList);/set some initial valuesfor(int i = 0; i /#include other libsBOOL API
12、ENTRY DllMain( HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)switch (ul_reason_for_call)case DLL_PROCESS_ATTACH:case DLL_THREAD_ATTACH:case DLL_THREAD_DETACH:case DLL_PROCESS_DETACH:break;return TRUE;std:string Recognise_Img(const std:string url)/返回结果return “从 dll 中返回的数据 . : “ +url;sta
13、tic PyObject* Recognise(PyObject *self, PyObject *args)const char *url;std:string sts;if (!PyArg_ParseTuple(args, “s“, sts = Recognise_Img(url);return Py_BuildValue(“s“, sts.c_str() );static PyMethodDef AllMyMethods = “Recognise“, Recognise, METH_VARARGS,NULL, NULL;extern “C“ PYUTIL_API void initpyU
14、til()PyObject *m, *d;m = Py_InitModule(“pyUtil“, AllMyMethods); d = PyModule_GetDict(m);3.6 在 Python环境中引用 C模块当生成 Python扩展模块的动态链接库后,就可以在 Python解释器中使用该扩展模块了,与 Python自带的模块一样,扩展模块也是通过 import命令引入后再使用的,以linux环境为例,如下所示:$ pythonPython 2.2.1 (#1, Aug 30 2002, 12:15:30)GCC 3.2 20020822 (Red Hat Linux Rawhide
15、3.2-4) on linux2Type “help“, “copyright“, “credits“ or “license“ for more information. import example example.fact(4)24四、C 访问 Python程序4.1 即时运行一段 Python程序#include “python.h“ int main(int argc, char * argv)Py_Initialize();PyRun_SimpleString(“print( “ Hello World “ )“ );Py_Finalize();system(“PAUSE“);re
16、turn 0;输出为Hello World注意在 Py_Initialize和 Py_Finalize之间的 Python程序处于同一运行环境int main(int argc, char * argv)Py_Initialize();PyRun_SimpleString(“str = “ Hello World “ “ );PyRun_SimpleString(“print(str)“ );Py_Finalize();system(“PAUSE“);return 0;这时输出仍然是 Hello World,说明定义 str和打印 str两段程序处于同一环境这里使用的 PyRun_Simple
17、String有一个缺点,文档中的描述是:Returns 0 on success or -1 if an exception was raised.也就是无法在 Python及 C语言中传递任何信息。而下面的函数即可实现更好的功能:PyObject* PyRun_String(const char *str, int start, PyObject *globals, PyObject *locals)例:#include “python.h“ int main(int argc, char * argv)Py_Initialize();PyRun_SimpleString(“x = 10“)
18、;PyRun_SimpleString(“y = 20“);/PyRun_SimpleString实际是将所有的代码都放在 _main_ 模块中运行PyObject* mainModule = PyImport_ImportModule(“_main_“);PyObject* dict = PyModule_GetDict(mainModule);PyObject* resultObject = PyRun_String(“x + y“ , Py_eval_input, dict, dict);if(resultObject)long result = PyLong_AsLong(result
19、Object);printf(“ %d “,result);Py_DECREF(resultObject);Py_Finalize();system(“PAUSE“);return 0 ;4.2从一个 Python脚本文件导入执行例 1file1 python file:存为 pytest.py,与 C程序放在同一目录下def add(a,b):print “in python function add“print “a = “ + str(a)print “b = “ + str(b)print “ret = “ + str(a+b)return a + bfile2 c source fi
20、le: c_call_python.c/C代码调用脚本里的 add函数#include #include #include #pragma comment(lib, “python26.lib“)int main(int argc, char* argv)/*在使用 Python系统前,必须使用 Py_Initialize对其进行初始化。它会载入Python的内建模块并添加系统路径到模块搜索路径中。这个函数没有返回值,检查系统是否初始化成功需要使用 Py_IsInitialized*/PyObject *pName, *pModule, *pDict, *pFunc, *pArgs, *pRe
21、tVal;Py_Initialize();/检查初始化是否成功if ( !Py_IsInitialized() ) return -1;/载入名为 pytest的脚本(注意:不是 pytest.py)pName = PyString_FromString(“pytest“);pModule = PyImport_Import(pName);if (!pModule)printf(“cant find pytest.py“);getchar();return -1;pDict = PyModule_GetDict(pModule);if ( !pDict ) return -1;/找出函数名为
22、add的函数pFunc = PyDict_GetItemString(pDict, “add“);if ( !pFunc | !PyCallable_Check(pFunc) )printf(“cant find function add“);getchar();return -1;/参数进栈pArgs = PyTuple_New(2);/*PyObject* Py_BuildValue(char *format, .)把 C+的变量转换成一个 Python对象。当需要从 C+传递变量到 Python时,就会使用这个函数。此函数有点类似 C的printf,但格式不同。常用的格式有s 表示字符串
23、,i 表示整型变量,f 表示浮点数,O 表示一个 Python对象*/PyTuple_SetItem(pArgs, 0, Py_BuildValue(“l“,3); PyTuple_SetItem(pArgs, 1, Py_BuildValue(“l“,4);/调用 Python函数pRetVal = PyObject_CallObject(pFunc, pArgs);printf(“function return value : %ldrn“, PyInt_AsLong(pRetVal);/减少引用计数,便于垃圾回收机制的清理Py_DECREF(pName);Py_DECREF(pArgs)
24、;Py_DECREF(pModule);Py_DECREF(pRetVal);/关闭 PythonPy_Finalize();return 0;例 2file1 python file: math_test.pydef add_func(a,b):return a+bdef sub_func(a,b):return (a-b)file2 c source file: c_call_python.c#include #include #include #include int main(int argc, char* argv)int arg0 = 0,arg1 = 0;if(argc = 3)
25、arg0 = atoi(argv1);arg1 = atoi(argv2);elseprintf(“please input 2 args!n“);return -1;Py_Initialize();if (!Py_IsInitialized()return -1;/添加模块所在路径,此处设置为当前目录PyRun_SimpleString(“import sys“);PyRun_SimpleString(“sys.path.append(./)“);PyObject *pModule;PyObject *pFunction;PyObject *pArgs;PyObject *pRetValue
26、;pModule = PyImport_ImportModule(“math_test“);if(!pModule)printf(“import python failed!n“);return -1;/此处和例 1中采用了不同的获取函数的方法pFunction = PyObject_GetAttrString(pModule, “add_func“);if(!pFunction)printf(“get python function failed!n“);return -1;pArgs = PyTuple_New(2);PyTuple_SetItem(pArgs, 0, Py_BuildVa
27、lue(“i“, arg0);PyTuple_SetItem(pArgs, 1, Py_BuildValue(“i“, arg1);pRetValue = PyObject_CallObject(pFunction, pArgs);if(pRetValue)printf(“%d + %d = %ldn“,arg0,arg1,PyInt_AsLong(pRetValue);Py_DECREF(pModule);Py_DECREF(pFunction);Py_DECREF(pArgs);Py_DECREF(pRetValue);Py_Finalize();return 0;例 3file1 pyt
28、hon file: test.py放在/test 目录下def test_add(a, b):print add, a, and, breturn a+bfile2 c source file:c文件放在任意位置#include using namespace std;int main()Py_Initialize();/将 Python工作路径切换到待调用模块所在目录,一定要保证路径名的正确性string path = “/test“;string chdir_cmd = string(“sys.path.append(“) + path + “)“;const char* cstr_cmd
29、 = chdir_cmd.c_str();PyRun_SimpleString(“import sys“);PyRun_SimpleString(cstr_cmd);/加载模块PyObject* moduleName = PyString_FromString(“test“); /模块名,不是文件名PyObject* pModule = PyImport_Import(moduleName);if (!pModule) cout “ERROR Python get module failed.“ endl;return 0;cout “INFO Python get module succee
30、d.“ endl;/加载函数PyObject* pv = PyObject_GetAttrString(pModule, “test_add“);if (!pv | !PyCallable_Check(pv) /验证是否加载成功cout “ERROR Cant find funftion (test_add)“ endl;return 0;cout “INFO Get function (test_add) succeed.“ endl;/设置参数PyObject* args = PyTuple_New(2); PyObject* arg1 = PyInt_FromLong(4);PyObject* arg2 = PyInt_FromLong(3);PyTuple_SetItem(args, 0, arg1);PyTuple_SetItem(args, 1, arg2);/调用函数PyObject* pRet = PyObject_CallObject(pv, args);/获取参数if(pRet)/验证是否调用成功long result = PyInt_AsLong(pRet);cout “result:“ result;Py_Finalize(); /释放资源return 0;