最近看開源項目時學習了一下用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 information 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; }
之後修改VC工程導出文件後綴名爲pyd
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: 找不到指定的程序。
結束語
以上的總結參考了部分網友的博文,主要如下,一併感謝: