




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
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开发实际软件系统时,很多时候都需要使用C/C+来对Python进行扩展。最常见的情况是目
3、前已经存在一个用C编写的库,需要在Python语言中使用该库的某些功能,此时就可以借助Python提供的扩展功能来实现。此外,由于Python从本质上讲还是一种脚本语言,某些功能用Python实现可能很难满足实际软件系统对执行效率的要求,此时也可以借助Python提供的扩展功能,将这些关键代码段用C或者C+实现,从而提供程序的执行性能。 本文主要介绍Python提供的C语言扩展接口,以及如何使用这些接口和C/C+语言来对Python进行功能性扩展,并辅以具体的实例讲述如何实现Python的功能扩展。 二、Python的C语言接口 Python是用C语言实现的一种脚本语言,本身具有优良的开放性和
4、可扩展性,并提供了方便灵活的应用程序接口(API),从而使得C/C+程序员能够在各个级别上对Python解释器的功能进行扩展。在使用C/C+对Python进行功能扩展之前,必须首先掌握Python解释所提供的C语言接口。 2.1 Python对象(PyObject) Python是一门面向对象的脚本语言,所有的对象在Python解释器中都被表示成PyObject,PyObject结构包含Python对象的所有成员指针,并且对Python对象的类型信息和引用计数进行维护。在进行Python的扩展编程时,一旦要在C或者C+中对Python对象进行处理,就意味着要维护一个PyObject结构。 在P
5、ython的C语言扩展接口中,大部分函数都有一个或者多个参数为PyObject指针类型,并且返回值也大都为PyObject指针。 2.2 引用计数 为了简化内存管理,Python通过引用计数机制实现了自动的垃圾回收功能,Python中的每个对象都有一个引用计数,用来计数该对象在不同场所分别被引用了多少次。每当引用一次Python对象,相应的引用计数就增1,每当消毁一次Python对象,则相应的引用就减1,只有当引用计数为零时,才真正从内存中删除Python对象。 下面的例子说明了Python解释器如何利用引用计数来对Pyhon对象进行管理:例1:refcount.pyclass refcoun
6、t: # etc.r1 = refcount() # 引用计数为1r2 = r1 # 引用计数为2del(r1) # 引用计数为1del(r2) # 引用计数为0,删除对象在C/C+中处理Python对象时,对引用计数进行正确的维护是一个关键问题,处理不好将很容易产生内存泄漏。Python的C语言接口提供了一些宏来对引用计数进行维护,最常见的是用Py_INCREF()来增加使Python对象的引用计数增1,用Py_DECREF()来使Python对象的引用计数减1。 2.3 数据类型 Python定义了六种数据类型:整型、浮点型、字符串、元组、列表和字典,在使用C语言对Python进行功能扩展
7、时,首先要了解如何在C和Python的数据类型间进行转化。 2.3.1 整型、浮点型和字符串 在Python的C语言扩展中要用到整型、浮点型和字符串这三种数据类型时相对比较简单,只需要知道如何生成和维护它们就可以了。下面的例子给出了如何在C语言中使用Python的这三种数据类型:例2:typeifs.c/ build an integerPyObject* pInt = Py_BuildValue("i", 2003);assert(PyInt_Check(pInt);int i = PyInt_AsLong(pInt);Py_DECREF(pInt);/ build a
8、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(pString);int nLen = PyString_Size(pString);char* s
9、= 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_Check(pTuple);assert(PyTuple_Size(pTuple) = 3);/ set th
10、e 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、i, &f, &s) PyErr_SetString(PyExc_TypeError, "invalid parameter");/ cleanupPy_DECREF(pTuple);2.3.3 列表 Python语言中的列表是一个长度可变的数组,列表比元组更为灵活,使用列表可以对其存储的Python对象进行随机访问。下面的例子示范了如何在C语言中使用Python的列表类型:例4:typelist.c/create the listPyObject* pList = PyList_New(3); /new referenceassert(PyList_Che
12、ck(pList);/set some initial valuesfor(int i = 0; i < 3; +i) PyList_SetItem(pList, i, Py_BuildValue("i", i);/insert an itemPyList_Insert(pList, 2, Py_BuildValue("s", "inserted");/append an itemPyList_Append(pList, Py_BuildValue("s", "appended");/so
13、rt the listPyList_Sort(pList);/reverse the listPyList_Reverse(pList);/fetch and manipulate a list slicePyObject* pSlice=PyList_GetSlice(pList,2,4); /new referencefor(int j = 0; j < PyList_Size(pSlice); +j) PyObject *pValue = PyList_GetItem(pList, j); assert(pValue);Py_DECREF(pSlice);/cleanupPy_DE
14、CREF(pList);2.3.4 字典 Python语言中的字典是一个根据关键字进行访问的数据类型。下面的例子示范了如何在C语言中使用Python的字典类型:例5:typedic.c/create the dictionaryPyObject* pDict = PyDict_New(); /new referenceassert(PyDict_Check(pDict);/add a few named valuesPyDict_SetItemString(pDict, "first", Py_BuildValue("i", 2003);PyDict_S
15、etItemString(pDict, "second", Py_BuildValue("f", 3.14f);/enumerate all named valuesPyObject* pKeys = PyDict_Keys(pDict); /new referencefor(int i = 0; i < PyList_Size(pKeys); +i) PyObject *pKey = PyList_GetItem(pKeys, i); PyObject *pValue = PyDict_GetItem(pDict, pKey); assert(p
16、Value);Py_DECREF(pKeys);/remove a named valuePyDict_DelItemString(pDict, "second");/cleanupPy_DECREF(pDict);2.4 程序交互接口1.void Py_Initialize( ) 初始化Python解释器,在C+程序中使用其它Python/C API之前,必须调用此函数,如果调用失败,将产生一个致命的错误。2. int PyRun_SimpleString( const char *command) 执行一段Python代码,就好象是在_main_ 函数里面执行一样。例:
17、PyRun_SimpleString("from time import time,ctimen""print ''Today is'',ctime(time()n");3. PyObject* PyImport_ImportModule( char *name) 导入一个Python模块,参数name可以是*.py文件的文件名。相当于Python内建函数_import_()。例:pmod = PyImport_ImportModule("mymod"); /mymod.py4. PyObject* P
18、yModule_GetDict( PyObject *module) 相当于Python模块对象的_dict_ 属性,得到模块名称空间下的字典对象。例:pdict = PyModule_GetDict(pmod);5. PyObject* PyRun_String( const char *str, int start, PyObject *globals, PyObject *locals) 执行一段Python代码。pstr = PyRun_String("message", Py_eval_input, pdict, pdict);6. int PyArg_Parse
19、( PyObject *args, char *format, .) 解构Python数据为C的类型,这样C程序中才可以使用Python里的数据。例:/* convert to C and print it*/PyArg_Parse(pstr, "s", &cstr);printf("%sn", cstr);7. PyObject* PyObject_GetAttrString( PyObject *o, char *attr_name) 返回模块对象o中的attr_name 属性或函数,相当于Python中表达式语句:o.attr_name。例
20、:/* to call mymod.transform(mymod.message) */pfunc = PyObject_GetAttrString(pmod, "transform");8. PyObject* Py_BuildValue( char *format, .) 构建一个参数列表,把C类型转换为Python对象,使Python可以使用C类型数据,例:cstr="this is hjs''s test, to uppercase"pargs = Py_BuildValue("(s)", cstr);9.
21、PyEval_CallObject(PyObject* pfunc, PyObject* pargs) 此函数有两个参数,都指向Python对象指针,pfunc是要调用的Python 函数,通常可用PyObject_GetAttrString()获得;pargs是函数的参数列表,通常可用Py_BuildValue()构建。例:pstr = PyEval_CallObject(pfunc, pargs);PyArg_Parse(pstr, "s", &cstr);printf("%sn", cstr);10. void Py_Finalize()
22、关闭Python解释器,释放解释器所占用的资源。三、Python访问C程序 3.1 模块封装 在了解了Python的C语言接口后,就可以利用Python解释器提供的这些接口来编写Python的C语言扩展,假设有如下一个C语言函数:例6:example.cint fact(int n) if (n <= 1) return 1; else return n * fact(n - 1); 该函数的功能是计算某个给定自然数的阶乘,如果想在Python解释器中调用该函数,则应该首先将其实现为Python中的一个模块,这需要编写相应的封装接口,如下所示:例7: wrap.c#include PyO
23、bject* wrap_fact(PyObject* self, PyObject* args) int n, result; if (!PyArg_ParseTuple(args, "i:fact", &n) return NULL; result = fact(n); return Py_BuildValue("i", result);static PyMethodDef exampleMethods = "fact", wrap_fact, METH_VARARGS, "Caculate N!", N
24、ULL, NULL;void initexample() PyObject* m; m = Py_InitModule("example", exampleMethods);一个典型的Python扩展模块至少应该包含三个部分:导出函数、方法列表和初始化函数3.2 导出函数 要在Python解释器中使用C语言中的某个函数,首先要为其编写相应的导出函数,上述例子中的导出函数为wrap_fact。在Python的C语言扩展中,所有的导出函数都具有相同的函数原型:PyObject* method(PyObject* self, PyObject* args);该函数是Python解
25、释器和C函数进行交互的接口,带有两个参数:self和args。参数self只在C函数被实现为内联方法(built- in method)时才被用到,通常该参数的值为空(NULL)。参数args中包含了Python解释器要传递给C函数的所有参数,通常使用Python的 C语言扩展接口提供的函数PyArg_ParseTuple()来获得这些参数值。 所有的导出函数都返回一个PyObject指针,如果对应的C函数没有真正的返回值(即返回值类型为void),则应返回一个全局的None对象(Py_None),并将其引用计数增1,如下所示:PyObject* method(PyObject *self,
26、PyObject *args) Py_INCREF(Py_None); return Py_None;3.3 方法列表 方法列表中给出了所有可以被Python解释器使用的方法,上述例子对应的方法列表为:static PyMethodDef exampleMethods = "fact", wrap_fact, METH_VARARGS, "Caculate N!", NULL, NULL;方法列表中的每项由四个部分组成:方法名、导出函数、参数传递方式和方法描述。方法名是从Python解释器中调用该方法时所使用的名字。参数传递方式则规定了Python向C函
27、数传递参数的具体形式,可选的两种方式是METH_VARARGS和METH_KEYWORDS,其中 METH_VARARGS是参数传递的标准形式,它通过Python的元组在Python解释器和C函数之间传递参数,若采用METH_KEYWORD方式,则Python解释器和C函数之间将通过Python的字典类型在两者之间进行参数传递。 3.4 初始化函数 所有的Python扩展模块都必须要有一个初始化函数,以便Python解释器能够对模块进行正确的初始化。Python解释器规定所有的初始化函数的函数名都必须以init开头,并加上模块的名字。对于模块example来说,则相应的初始化函数为:void
28、initexample() PyObject* m; m = Py_InitModule("example", exampleMethods);当Python解释器需要导入该模块时,将根据该模块的名称查找相应的初始化函数,一旦找到则调用该函数进行相应的初始化工作,初始化函数则通过调用Python的C语言扩展接口所提供的函数Py_InitModule(),来向Python解释器注册该模块中所有可以用到的方法。 3.5 编译链接 3.5.1 在Linux下生成python扩展库要在Python解释器中使用C语言编写的扩展模块,必须将其编译成动态链接库的形式。下面以RedHat
29、Linux 8.0为例,介绍如何将C编写的Python扩展模块编译成动态链接库:$gcc fpic c -I/usr/include/python2.2 -I/usr/lib/python2.2/config example.c wrapper.c$ gcc -shared -o example.so example.o wrapper.o3.5.2 在Windows下生成python扩展库首先建立一个dll工程,以一个例子来说明工程结构#ifdef PYUTIL_EXPORTS#define PYUTIL_API _declspec(dllexport)#else#define PYUTIL
30、_API _declspec(dllimport)#endif#include<python.h>/#include other libsBOOL APIENTRY 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
31、TRUE;std:string Recognise_Img(const std:string url) /返回结果 return "从dll中返回的数据. : " +url;static PyObject* Recognise(PyObject *self, PyObject *args) const char *url; std:string sts; if (!PyArg_ParseTuple(args, "s", &url) return NULL; sts = Recognise_Img(url); return Py_BuildValu
32、e("s", sts.c_str() );static PyMethodDef AllMyMethods = "Recognise", Recognise, METH_VARARGS, NULL, NULL;extern "C" PYUTIL_API void initpyUtil() PyObject *m, *d; m = Py_InitModule("pyUtil", AllMyMethods); d = PyModule_GetDict(m);3.6 在Python环境中引用C模块当生成Python扩展模块
33、的动态链接库后,就可以在Python解释器中使用该扩展模块了,与Python自带的模块一样,扩展模块也是通过import命令引入后再使用的,以linux环境为例,如下所示:$ pythonPython 2.2.1 (#1, Aug 30 2002, 12:15:30)GCC 3.2 20020822 (Red Hat Linux Rawhide 3.2-4) on linux2Type "help", "copyright", "credits" or "license" for more information.
34、>>> 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( &q
35、uot; Hello World " )" ); Py_Finalize(); system("PAUSE"); return 0;输出为Hello World注意在Py_Initialize和Py_Finalize之间的Python程序处于同一运行环境int main(int argc, char * a
36、rgv) Py_Initialize(); PyRun_SimpleString("str = " Hello World " " ); PyRun_SimpleString("print(str)" ); Py_Finalize(); system
37、("PAUSE"); return 0;这时输出仍然是Hello World,说明定义str和打印str两段程序处于同一环境这里使用的PyRun_SimpleString有一个缺点,文档中的描述是:Returns 0 on success or -1 if an exception was raised.也就是无法在Python及C语言中传递任何信息。而下面的函数即可实现更好的功能:PyObject* PyRun_String(const char *str, int start
38、, PyObject *globals, PyObject *locals)例:#include "python.h" int main(int argc, char * argv) Py_Initialize(); PyRun_SimpleString("x = 10"); PyRun_SimpleString("y = 2
39、0");/PyRun_SimpleString实际是将所有的代码都放在 _main_ 模块中运行 PyObject* mainModule = PyImport_ImportModule("_main_"); PyObject* dict = PyModule_GetDict(mainModule); PyObject* resultObject = PyRun_String("x + y"
40、; , Py_eval_input, dict, dict); if(resultObject) long result = PyLong_AsLong(resultObject); printf(" %d ",result);
41、60; Py_DECREF(resultObject); Py_Finalize(); system("PAUSE"); return 0 4.2从一个Python脚本文件导入执行例1file1 python file:存为pytest.py,与C程序放在同一目录下def add(a,b):
42、 print "in python function add" print "a = " + str(a) print "b = " + str(b) print "ret = " + str(a+b) return a + bfile2 c source file: c_call_python.c/C代码调用脚本里的add函数#incl
43、ude <stdio.h>#include <stdlib.h>#include <python.h>#pragma comment(lib, "python26.lib")int main(int argc, char* argv) /*在使用Python系统前,必须使用Py_Initialize对其进行初始化。它会载入Python的内建模块并添加系统路径到模块搜索路径中。这个函数没有返回值,检查系统是否初始化成功需要使用Py_IsInitialized*/ PyO
44、bject *pName, *pModule, *pDict, *pFunc, *pArgs, *pRetVal; Py_Initialize(); /检查初始化是否成功 if ( !Py_IsInitialized() ) return -1; /载入名为pytest的脚本(注意:
45、不是pytest.py) pName = PyString_FromString("pytest"); pModule = PyImport_Import(pName); if (!pModule) printf("can't find pytest.py");
46、 getchar(); return -1; pDict = PyModule_GetDict(pModule); if ( !pDict ) return -1; /找出函
47、数名为add的函数 pFunc = PyDict_GetItemString(pDict, "add"); if ( !pFunc | !PyCallable_Check(pFunc) ) printf("can't find function add"); getchar
48、(); return -1; /参数进栈 pArgs = PyTuple_New(2);/*PyObject* Py_BuildValue(char *format, .)把C+的变量转换成一个Python对象。当需要从C+传递变量到Python时,就会使用这个函数。此函数有点类似C的printf,但格式不同。常用的格式有 s 表示字符串, i 表示整型变量,
49、0; 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);
50、0; printf("function return value : %ldrn", PyInt_AsLong(pRetVal);/减少引用计数,便于垃圾回收机制的清理 Py_DECREF(pName); Py_DECREF(pArgs); Py_DECREF(pModule); Py_DECREF(pRetVal); /关闭Python Py_Fi
51、nalize(); 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 <stdio.h>#include <stdlib.h>#include <string.h>#include <Python.h>int main(int argc, char
52、* argv) int arg0 = 0,arg1 = 0; if(argc = 3) arg0 = atoi(argv1); arg1 = atoi(argv2); else printf("please input 2 args!n"); return -1; Py_Initiali
53、ze(); if (!Py_IsInitialized()return -1;/添加模块所在路径,此处设置为当前目录 PyRun_SimpleString("import sys"); PyRun_SimpleString("sys.path.append('./')"); PyObject *pModule; PyObject *pFunction; PyObject *pArgs;
54、 PyObject *pRetValue; 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_BuildValue(&quo
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 西方国家政策协调的机制分析试题及答案
- 机电工程高新技术考察试题及答案
- 2025年直播电商主播影响力提升与内容营销策略研究报告
- 反映西方社会变迁的重大政治事件试题及答案
- 公共政策在应对自然灾害中的角色研究试题及答案
- 网络设备性能评估试题及答案
- 接受失败并调整学习方法2025年信息系统项目管理师试题及答案
- 西方国家的社会政策与民生福祉试题及答案
- 沟通技巧在公共政策中的应用研究试题及答案
- 机电接口与通讯协议试题及答案
- 某村古建筑保护建设工程项目可行性方案
- 安全生产知识竞赛题库及答案(共200题)
- 2023年中电信数智科技有限公司招聘笔试题库及答案解析
- GB 1886.358-2022食品安全国家标准食品添加剂磷脂
- GB/T 1508-2002锰矿石全铁含量的测定重铬酸钾滴定法和邻菲啰啉分光光度法
- 小学六年级信息技术复习题
- 食品安全培训(食品安全知识)-课件
- 初二物理新人教版《功》公开课一等奖省优质课大赛获奖课件
- 北京大学国际政治经济学教学大纲
- 合肥市建设工程消防设计审查、消防验收、备案与抽查文书样式
- 《电气工程基础》熊信银-张步涵-华中科技大学习题答案全解
评论
0/150
提交评论