一種是Python文檔的推薦方案,採用C API擴充寫法:
在D:建一個add.c檔案,輸入
//add.c</p><p>//</p><p>#include <Python.h>;</p><p>static PyObject* add(PyObject *self, PyObject *args);<br />//一定聲明為static,把他們限制在這個檔案範圍裡。 幾乎所有的參數都是PyObject類型。 在python,每個東西都是object。 </p><p>static PyObject* add(PyObject* self, PyObject* args)<br />{<br /> int x=0 ;<br /> int y=0;<br /> int z=0;<br /> if (! PyArg_ParseTuple(args, "i|i", &x, &y))<br /> return NULL;<br /> /*第一個參數是self,這個是python用的, 每個函數都要有。我們暫時不管。args是一個參數列表。她把所有的參數都整合成一個string。所以<br /> 我們需要從這個string裡來解析我們的參數。PyArg_ParseTuple來完成這個任務。第一個參數是args, 就是我們要轉換的參數。第二個是格式符號。<br /> “s”代表是個string。 從args裡提取一個參數就寫"s", 兩個的話就寫"s|s", 如果是一個string,一個int,就寫"s|i", 和printf差不多。第三個<br /> 參數就是提取出來的參數放置的真正位置。必須傳遞這個參數的地址。對於add, 他將提取兩個參數。分別是x和y。*/<br />z=x+y;<br />return Py_BuildValue("i", z);<br /> /*調用完之後我們需要返回結果。這個結果是c的type或者是我們自己定義的類型。必須把他轉換成PyObject, 讓python認識。這個用Py_BuildValue<br /> 來完成。他是PyArg_ParseTuple的逆過程。他的第一個參數和PyArg_ParseTuple的第二個參數一樣, 是個格式化符號。第三個參數<br /> 是我們需要轉換的參數。Py_BuildValue會把所有的返回只組裝成一個tutple給python。*/<br />} </p><p>static PyMethodDef addMethods[] =<br />{<br /> {"add", add, METH_VARARGS, "Execute a shell command."},<br /> {NULL, NULL, 0, NULL}<br />};<br />/*這個是一個c的結構。他來完成一個映射。 我們需要把我們擴充的函數都映射到這個表裡。表的第一個欄位是python真正認識的。是python<br />裡的方法名字。 第二個欄位是python裡的這個方法名字的具體實現的函數名。 在python裡調用add, 真正執行的是用c寫的add函數。<br />第三個欄位是METH_VARARGS, 他告訴python,add是調用c函數來實現的。第四個欄位是這個函數的說明。如果你在python裡來help這個函數,<br />將顯示這個說明。相當於在python裡的函數的文檔說明。*/</p><p>PyMODINIT_FUNC initadd()<br />{<br /> Py_InitModule("add", addMethods);<br />}<br />/*注意,這個函數的名字不能改動。 必須是init+模組名字。 我們的模組名字是add。所以這個函數是initadd()。<br />這樣python在匯入add 的模組時候,才會找到這個函數,並調用。這個函數調用Py_InitModule來將模組名字和映射表結合在一起。<br />他表示,add這個模組使用addMethods這個映射表。python應該這樣匯入我們的module的.*/</p><p>
然後在D:盤建立setup.py
from distutils.core import setup, Extension</p><p>module1 = Extension('add', sources = ['add.c'])</p><p>setup (name = 'PackageName', version = '1.0', description = 'This is a ADD package', ext_modules = [module1])</p><p>
將cmd切換到D:
輸入setup.py build --compiler=mingw32 -verbose
這裡我選擇了mingw32,否則在我的機器上它會用msvc,而這會有一些小錯誤
運行成功後會在(目前的目錄的)D:/build/lib.win32-2.6下產生add.pyd。
下面就在Python中匯入
>>> import sys
>>> sys.path.append("D:/build/lib.win32-2.6")
>>> import add
>>> add.add(3,2)
5
Bingo!
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
以上是用C API的擴充方式,它的寫法比較麻煩。以下用ctypes直接匯入dll模組。
如果是在Windows下面:
首先在D:建立檔案spam.c
//spam.c</p><p>//</p><p>#include <windows.h></p><p>BOOL APIENTRY<br />DllMain(HANDLE hModule, DWORD dwReason, LPVOID lpReserved)<br />{<br /> return TRUE;<br />}</p><p>__declspec(dllexport) int<br />multiply(int num1, int num2)<br />{<br /> return num1 * num2;<br />}</p><p>//這是一個標準的Windows DLL的寫法。</p><p>
我用cl來編譯它,現在我漸漸喜歡上了用cl來編譯,省得建一個好幾兆的項目,而且參數可以靈活選擇。後面我會繼續學習如何寫make file。
在cmd輸入cl -I"d:/programs/python26/include" spam.cpp,會產生spam.obj。
再輸入link spam.obj /DLL /libpath:"d:/programs/python26/libs",產生spam.dll。
下面到python中匯入:
>>> import ctypes
>>> cdll=ctypes.cdll.LoadLibrary('d:/spam.dll')
>>> cdll.multiply(2,3)
6
如果是在Linux下面:
在/home/Henry下建立spam.c
char *foo(){<br /> char *p = "hello world";<br /> return p;<br />} </p><p>void foo1(char *p){<br /> strcpy(p, "hello world");<br />}<br />
編譯:$ gcc -o libspam.so -fpic -shared spam.c
進入Python:
#返回指標:<br />>>> import ctypes<br />>>> hello = ctypes.cdll.LoadLibrary("/home/Henry/libspam.so")<br />>>> p = hello.foo()<br />>>> ctypes.c_char_p(p).value<br />>>> 'hello world' </p><p>#傳入buffer:<br />>>> b = ctypes.create_string_buffer(12)<br />>>> hello.foo1(b)<br />>>> b.value<br />>>> 'hello world'<br />
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
但是ctypes對C++的DLL匯入不行。對於C++庫的匯入還是要藉助於Boost.python。
我一定會回來的!