Python 3.1 用C寫模塊 擴展(序)

注意。此方式只在win下用vc2008成功。其餘編譯器/環境未試驗:

1。 在vc中創建一個dll的project。 假定咱們的module的名字是mytest.那麼咱們的dll名字就是mytest.dll。 這個是必須的。
2。 用c寫咱們想要實現的部分。步驟以下 python

 

#include <Python.h> //這個是必須的。python的類型都在這裏定義。
static PyObject* my_strlen(PyObject *self, PyObject *args) { char *string; int len; if (!PyArg_ParseTuple(args, "s", &string)) return NULL; len = strlen(string); return Py_BuildValue("i", len); } static PyObject* my_strcat(PyObject *self, PyObject *args) { char* string1; char* string2; char* newstring; if (!PyArg_ParseTuple(args, "s|s", &string1, &string2)) return NULL; newstring = strcat(string1, string2); return Py_BuildValue("s", newstring); }

 

 

第一個參數是self,這個是python用的, 每一個函數都要有。咱們暫時無論。args是一個參數列表。她把全部的參數都整合成一個string。因此
咱們須要從這個string裏來解析咱們的參數。
PyArg_ParseTuple來完成這個任務。第一個參數是args, 就是咱們要轉換的參數。第二個是格式符號。「s」表明是個string。 從args裏
提取一個參數就寫"s", 兩個的話就寫"s|s", 若是是一個string,一個int,就寫"s|i", 和printf差很少。第三個參數就是提取出來的
參數放置的真正位置。必須傳遞這個參數的地址。對於my_strcat, 他將提取兩個參數。分別是string1和string2。

而後調用真正的咱們的實現。分別是strlen和strcat。
調用完以後咱們須要返回結果。這個結果是c的type或者是咱們本身定義的類型。必須把他轉換成PyObject, 讓python認識。這個用Py_BuildValue
來完成。他是PyArg_ParseTuple的逆過程。他的第一個參數和PyArg_ParseTuple的第二個參數同樣, 是個格式化符號。第三個參數
是咱們須要轉換的參數。Py_BuildValue會把全部的返回只組裝成一個tutple給python。

實際上,咱們只是把咱們想要實現的部分調用python提供的api來封裝了一下。可是python並不知道怎麼用這些函數。
咱們還須要作一些工做。 api

 

static PyMethodDef mytestMethods[] = { {"mystrlen",  my_strlen, METH_VARARGS, "We test strlen of C"}, {"mystrcat",  my_strcat, METH_VARARGS, "We test strcat of C"}, {NULL, NULL, 0, NULL} };

 

這個是一個c的結構。他來完成一個映射。 咱們須要把咱們擴展的函數都映射到這個表裏。表的第一個字段是python真正認識的。是python
裏的方法名字。 第二個字段是python裏的這個方法名字的具體實現的函數名。 在python裏調用mystrlen, 真正執行的是用c寫的
my_strlen函數。第三個字段是METH_VARARGS, 他告訴python,mystrlen是調用c函數來實現的。第四個字段是這個函數的說明。若是你在
python裏來help這個函數,將顯示這個說明。至關於在python裏的函數的文檔說明。

咱們創建了這個映射表。這個表是在python導入咱們的這個module的時候來用的。函數

 

/*下面就和2.x版本的約定不一樣了*/
static PyMethodDef mytestMethods[] = { {"mystrlen", my_strlen, METH_VARARGS, "We test strlen of C"}, {"mystrcat", my_strcat, METH_VARARGS, "We test strcat of C"}, {NULL, NULL, 0, NULL} }; char name[] = "mytest"; static PyModuleDef mytestModule; //對於其餘平臺。函數的修飾符可能不一樣
_declspec(dllexport)void PyInit_mytest() { mytestModule.m_methods = mytestMethods;//模塊的方法表
    mytestModule.m_name = name;//模塊說明
    PyModule_Create2(&mytestModule, PYTHON_API_VERSION); }

 

注意,這個函數的名字不能改動。 必須是PyInit_+模塊名字。 咱們的模塊名字是mytest。因此這個函數是PyInit_mytest()。 這個函數應該被 導出。因此使用 _declspec(dllexport)。 這樣python在導入mytest 的模塊時候,纔會找到這個函數,並調用。

a. 當咱們import mytest的時候。 python裝載這個module的dll。 在這裏是mytest.dll
b. 而後在這個dll裏調用, PyInit_mytest函數來創建一個映射表。 若是這個initmytest函數不實現或者沒有導出。就不能成功把這個module
導入到python裏。
c. python 從映射表 知道這個模塊實現了幾個方法。而且名字分別是什麼。當調用他們的時候,找到相應的c的函數。

寫完這些代碼之後,咱們能夠在vc裏編譯這個dll, 注意,必須編譯成release版本。編譯的dll名字是mytest.dll。若是不是的話,在vc裏改動link的設置。 測試

 

把mytest.dll更名爲mytest.pyd,而後拷到python3的DLLs目錄下ui

 

#test.py 測試代碼 import mytest print(mytest.mystrlen("123") ) print(mytest.mystrcat("123", "456"))

 

 

應該輸出:
3
123456spa

相關文章
相關標籤/搜索