Python遇到性能瓶颈怎么办?答案就是找一个具有相应功能的C/C++程序,编译成CPython模块,用于Python调用,提高性能。如何将C/C++程序编译成Python模块。例如在用于科学计算的Python中,用于数据处理的Numpy模块是用C语言编写的。Numpy的处理速度是Pandas的数倍。Numpy的处理速度并不比go语言差。本文主要介绍如何将C/C++程序编译成Python模块。本文技术性较强,需要耐心阅读。作为一种胶水语言,Python可以很容易地通过C/C++进行扩展以提高性能。之前写过一篇文章,介绍如何通过Python的ctypes加载常用的.so库。事实上,这并不是真正用C/C++编写的Python扩展模块。本文将介绍如何使用C语言和C++编写Python模块。1、Python的C语言接口Python语言最初是一种用C语言实现的脚本语言,后来因为后来用它的语言实现了Python而被称为CPython,比如用Python实现的Python——PyPy,用Java语言实现的Python——Jython,Python由.Net-IronPython实现。CPython具有优良的开放性和可扩展性,提供了方便灵活的应用程序编程接口(API),使C/C++程序员能够扩展Python解释器的功能。Python的C语言接口非常适合封装各种用C语言实现的功能。如果要封装C++类,使用boost_python或者SWIG更方便更合适。还有一个类似于boost_python的pybind11,支持C++11。1模块封装假设我们有一个C函数:/*文件名:mylib.c*/intaddone(inta){returna+1;}如果要在Python解释器中调用这个函数,首先应该将其实现为一个模块,其中需要编写相应的包接口,如下:(args,"i:fact",&n))returnNULL;result=addone(n);/*这里调用了C函数*/returnPy_BuildValue("i",result);}staticPyMethodDefmylibMethods[]={{"addone",wrap_addone,METH_VARARGS,"AddonetoN"},{NULL,NULL}};voidinitmylib(){PyObject*m;m=Py_InitModule("mylib",mylibMethods);}以上是一个典型的Python扩展模块,至少应该包含三部分:导出函数、方法列表和初始化函数。2导出函数要在Python解释器中调用C语言的函数,首先要为其编写相应的导出函数,上例中的导出函数为wrap_addone。在Python的C语言扩展中,所有导出的函数都有相同的函数原型:PyObject*wrap_method(PyObject*self,PyObject*args);thisfunction是Python解释器和C函数的接口,一般用wrapped_开头后跟C语言的函数名,这样名字就和导出的函数和C语言函数一一对应,使代码更清晰。它有两个参数:self和args。参数self仅在C函数作为内置方法实现时使用,通常该参数的值为空(NULL)。参数args包含了Python解释器将传递给C函数的所有参数,通常使用Python的C语言扩展接口提供的函数PyArg_ParseTuple()来获取这些参数值。所有导出的函数都返回一个PyObject指针。如果对应的C函数没有真正的返回值(即返回值类型为void),则应该返回一个全局的None对象(Py_None),并将其引用计数加1,如下所示:PyObject*wrap_method(PyObject*self,PyObject*args){Py_INCREF(Py_None);returnPy_None;}3方法列表方法列表列出了Python解释器可以使用的所有方法。上面例子对应的方法列表是:staticPyMethodDefmylibMethods[]={{"addone",wrap_addone,METH_VARARGS,"AddonetoN"},{NULL,NULL}};方法列表中的每一项由四部分组成:方法名导出函数参数传递方法方法描述方法名是从Python解释器调用方法时使用的名称。参数传递方式规定了Python向C函数传递参数的具体形式。两个可选的方法是METH_VARARGS和METH_KEYWORDS,其中METH_VARARGS是参数传递的标准形式,通过Python元组在Python解释器和C函数之间传递。如果使用METH_KEYWORD方法,Python解释器和C函数会通过Python字典类型在两者之间传递参数。4初始化函数所有的Python扩展模块都必须有一个初始化函数,这样Python解释器才能正确地初始化模块。Python解释器规定所有初始化函数的函数名必须以init开头,加上模块名。对于模块mylib,对应的初始化函数为:voidinitmylib(){PyObject*m;m=Py_InitModule("mylib",mylibMethods);}当Python解释器需要导入模块时,会根据module对应的初始化函数,一旦找到,就会调用执行相应的初始化工作,初始化函数会通过调用Python的C语言扩展接口提供的函数Py_InitModule(),向Python解释器注册模块中所有可用的模块。方法。5编译链接要在Python解释器中使用C语言编写的扩展模块,必须编译成动态链接库的形式。下面以Linux为例介绍如何将C语言编写的Python扩展模块编译成动态链接库:$gcc-fpic-shared-omylib.so-I/usr/include/python2.7mylib.cwrap_mylib.c6调用上面在Python中编译好的Python扩展模块的动态链接库可以直接在Python中导入。如下:veelion@gtx:~$pythonPython2.7.12(default,Nov192016,06:48:10)[GCC5.4.020160609]onlinux2Type"help","copyright","credits"or"license"formoreinformation.>>>importexample>>>example.addone(7)8>>>>>>这里生成的.so动态库和上一篇没有Python的C语言生成的动态库不同,从生成过程和使用方法可以看到这里的动态库使用起来感觉就像一个Python模块,直接导入即可。2.使用boost_python库封装C++类安装boostpython库:sudoaptitudeinstalllibboost-python-devexample下面的代码简单实现了一个常用函数maxab()和一个Student类:#include#includeintmaxab(inta,intb){returna>b?a:b;}classStudent{private:intage;std::stringname;public:Student(){}Student(std::stringconst&_name,int_age){name=_name;age=_age;}staticvoidmyrole(){std::cout<<"我是学生!"<
