最近看开源项目时学习了一下用C/C++写python模块,顺便把学习进行一下总结,废话少说直接开始:
环境:windows、python2.78、VS2010或MingW
1 创建VC工程
(1) 打开VC6.0或VS2008,然后File-->New-->Project-->Win32 DLL Project。建立一个Empty Project,比如testClass,一路确定。
(2) 之后向工程添加python头文件目录及库文件目录,如头文件目录:F:\python278\include,库文件目录:F:\python278\libs
(3) 添加一个C++或C源文件,如工程中有用到类,则添加的必须是C++文件,这里直接添加main.cpp
#include <Python.h> #include <iostream> #include <sstream> #include <structmember.h> #include <windows.h> using namespace std; typedef struct _Cscore { PyObject_HEAD char *m_szName; float m_dMath; float m_dEnglish; }Cscore; static PyMemberDef Cscore_DataMembers[] = { //类/结构的数据成员的说明. {"m_szName", T_STRING, offsetof(Cscore, m_szName), 0, "The Name of instance"}, {"m_dMath", T_FLOAT, offsetof(Cscore, m_dMath), 0, "The Math score of instance."}, {"m_dEnglish", T_FLOAT, offsetof(Cscore, m_dEnglish), 0, "The English score of instance."}, {NULL, NULL, NULL, 0, NULL} }; ////////////////////////////////////////////////////////////// // Cscore类的所有内置、构造方法. // static void Cscore_init(Cscore* Self, PyObject* pArgs) //构造方法. { const char* Name = 0; if(!PyArg_ParseTuple(pArgs, "sff", &Name, &Self->m_dMath, &Self->m_dEnglish)) { cout<<"Parse the argument Failed! You should pass correct values!"<<endl; return ; } Self->m_szName = new char[strlen(Name) + 1]; strcpy(Self->m_szName, Name); } static void Cscore_Destruct(Cscore* Self) //析构方法. { if(Self->m_szName){ delete [] Self->m_szName; //先释放其字符指针对象. } OutputDebugString(TEXT("destroy!!!")); //如果还有PyObject*成员的话,要一并释放之. //如:Py_XDECREF(Self->Member); Py_TYPE(Self)->tp_free((PyObject*)Self); //释放对象/实例. } static PyObject* Cscore_Str(Cscore* Self) //调用str/print时自动调用此函数. { ostringstream OStr; OStr<<"Name : "<<Self->m_szName<<endl <<"Math : "<<Self->m_dMath<<endl <<"English : "<<Self->m_dEnglish<<endl; string Str = OStr.str(); return Py_BuildValue("s", Str.c_str()); } static PyObject* Cscore_Repr(Cscore* Self) //调用repr内置函数时自动调用. { return Cscore_Str(Self); } //////////////////////////////////////////////////////////// // Cscore类的所有Get方法. // static PyObject* Cscore_GetName(Cscore* Self) { return Py_BuildValue("s", Self->m_szName); } static PyObject* Cscore_GetMath(Cscore* Self) { return Py_BuildValue("f", Self->m_dMath); } static PyObject* Cscore_GetEnglish(Cscore* Self) { return Py_BuildValue("f", Self->m_dEnglish); } //////////////////////////////////////////////////////////// // Cscore类的所有Set方法. // static PyObject* Cscore_SetMath(Cscore* Self, PyObject* Argvs) { Py_INCREF(Py_None); if(!PyArg_ParseTuple(Argvs, "f", &Self->m_dMath)) { cout<<"Parse the argument Failed! You should pass correct values!"<<endl; return Py_None; } return Py_None; } static PyObject* Cscore_SetEnglish(Cscore* Self, PyObject* Argvs) { Py_INCREF(Py_None); if(!PyArg_ParseTuple(Argvs, "f", &Self->m_dEnglish)) { cout<<"Parse the argument Failed! You should pass correct values!"<<endl; return Py_None; } return Py_None; } static PyObject* Cscore_PrintInfo(Cscore* Self) { cout<<"The scores as follows:"<<endl <<"=============================="<<endl <<"Name : "<<Self->m_szName<<endl <<"Math : "<<Self->m_dMath<<endl <<"English : "<<Self->m_dEnglish<<endl <<"=============================="<<endl; Py_XINCREF(Py_None); return Py_None; } static PyMethodDef Cscore_MethodMembers[] = //类的所有成员函数结构列表. { {"GetName", (PyCFunction)Cscore_GetName, METH_NOARGS, "Get the name of instance."}, {"GetMath", (PyCFunction)Cscore_GetMath, METH_NOARGS, "Get the math score of instance."}, {"GetEnglish", (PyCFunction)Cscore_GetEnglish, METH_NOARGS, "Get the english score of isntance."}, {"SetMath", (PyCFunction)Cscore_SetMath, METH_VaraRGS, "Set the math score of instance."}, {"SetEnglish", (PyCFunction)Cscore_SetEnglish, METH_VaraRGS, "Set the english of instance."}, {"PrintInfo", (PyCFunction)Cscore_PrintInfo, METH_NOARGS, "Print all @R_497_4045@ion of instance."}, {NULL, NULL, NULL, NULL} }; //////////////////////////////////////////////////////////// // 类/结构的所有成员、内置属性的说明信息. // static PyTypeObject Cscore_ClassInfo = { PyVarObject_HEAD_INIT(NULL, 0) "Module.MyCppClass", //可以通过__class__获得这个字符串. CPP可以用类.__name__获取. sizeof(Cscore), //类/结构的长度.调用PyObject_New时需要知道其大小. 0, (destructor)Cscore_Destruct, //类的析构函数. 0, 0, 0, 0, (reprfunc)Cscore_Repr, //repr 内置函数调用。 0, 0, 0, 0, 0, (reprfunc)Cscore_Str, //Str/print内置函数调用. 0, 0, 0, Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, //如果没有提供方法的话,为Py_TPFLAGS_DEFAULE "MyCppClass Objects---Extensioned by C++!", //__doc__,类/结构的DocString. 0, 0, 0, 0, 0, 0, Cscore_MethodMembers, //类的所有方法集合. Cscore_DataMembers, //类的所有数据成员集合. 0, 0, 0, 0, 0, 0, (initproc)Cscore_init, //类的构造函数. 0, }; //////////////////////////////////////////////////////////// // 此模块的说明信息. 由于我用的python2所以直接把该部分进行注释 // /*static PyModuleDef ModuleInfo = { PyModuleDef_HEAD_INIT, "My C++ Class Module", //模块的内置名--__name__. "This Module Created By C++--extension a class to Python!", //模块的DocString.__doc__ -1, NULL, NULL, NULL, NULL, NULL };*/ int add(int x, int y) { return x+y; } static PyObject* W_add(PyObject* self, PyObject* args) { int x; int y; if(!PyArg_ParseTuple(args, "i|i", &x, &y)) { return NULL; } else { return Py_BuildValue("i", add(x, y)); } } static PyMethodDef module_methods[] = {{"add", W_add, METH_VaraRGS, "a function from C"},{NULL, NULL, 0, NULL}}; PyMODINIT_FUNC inittestClass(void) //模块外部名称为--CppClass { PyObject* pReturn = 0; Cscore_ClassInfo.tp_new = PyType_GenericNew; //此类的new内置函数—建立对象. ///////////////////////////////////////////////////// // 完成对象类型的初始化—包括添加其继承特性等等。 // 如果成功,则返回0,否则返回-1并抛出异常. if(PyType_Ready(&Cscore_ClassInfo) < 0) return; pReturn = Py_InitModule3("testClass", module_methods, "Example module that creates an extension type."); if(pReturn == NULL) return; Py_INCREF(&Cscore_ClassInfo); PyModule_Addobject(pReturn, "Cscore", (PyObject*)&Cscore_ClassInfo); //将这个类加入到模块的Dictionary中. return; }
2 使用testClass模块
编译工程生成testClass.pyd模块文件,进入到导出文件目录,并启动python
导入testClass模块并查看其详细信息
模块详细使用
3 其他编译方式
下面介绍另一种用Python Script来生成.pyd文件的方法,新建一个Python脚本——CreatePyd.py,其内容如下:
from distutils.core import setup, Extension ModuleInfo = Extension("testClass", sources = [r"main.cpp"]) setup(name = "testClass", version = "1.0", description = "This module created by C++ weiwei.Zhao", author = 'weiwei.zhao', author_email = '[email protected]', license = "You can copy this program to anywhere.", url = "http://zhaoweiwei.top", long_description = '''This is really just a demo!''', platforms = "Windows", ext_modules = [ModuleInfo] )
内容解释:先导入所需的python模块,然后用Extension函数关联一个Cpp源文件一个要生成的模块名——注意:没有.pyd后缀。然后调用setup函数生成一个名字name为testClass的模块,版本version为1.0,描述description,作者信息author,作者邮箱author_email,还其平台platforms等等有用的信息!
最后调用:python CreateDLL.py build
经过编译后,就会新生成一个build目录,在build/lib.win32-2.7下你可以找到testClass.pyd文件。
需要注意的是编译脚本默认使用VS编译器,所以电脑上要安装VS,如果电脑上没有安装VS而是有MingW环境,则类似的可以使用MingW环境中gcc进行编译:
注意:为了使相关程序都能顺利找见,上图是在MingW的Shell命令行,而不是普通的windows Command命令行。
4 模块部署安装
一般来说,setup.py参数说明
#python setup.py build # 编译
#python setup.py install # 安装
#python setup.py sdist # 生成压缩包(zip/tar.gz)
#python setup.py bdist_wininst #生成NT平台安装包(.exe)
#python setup.py bdist_rpm #生成rpm包
或者直接"bdist 包格式",格式如下:
#python setup.py bdist --help-formats
--formats=rpm RPM distribution
--formats=gztar gzip'ed tar file
--formats=bztar bzip2'ed tar file
--formats=ztar compressed tar file
--formats=tar tar file
--formats=wininst Windows executable installer
--formats=zip ZIP file
下图说明了windows安装程序生成过程,在dist目录下有windows的安装程序testClass-1.0.win32-py2.7.exe
需要说明的是如果生成的testClass.pyd依赖于其他库文件,如在我的环境下用MingW生成的testClass.pyd要依赖libgcc_s_dw2-1.dll和libstdc++-6.dll两个文件,生成安装包时需把这两个文件和testClass.pyd放到一起,如放到build/lib.win32-2.7目录下,之后生成的安装文件testClass-1.0.win32-py2.7.exe会包含着两个文件,如果不这样做在导入testClass时会导致导入失败,提示类似:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ImportError: DLL load Failed: 找不到指定的程序。
结束语
以上的总结参考了部分网友的博文,主要如下,一并感谢:
http://blog.csdn.net/arnozhang12/article/details/5409155
http://blog.csdn.net/carolzhang8406/article/details/6925745
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。