不依靠其餘工具,直接使用Python的擴展API來編寫一些簡單的C擴展模塊。html
本篇參考PythonCookbook第15節和Python核心編程完成,值得注意的是,Python2.X和Python3.X在擴展庫寫法上略有不一樣,咱們研究的是3.X寫法。python
Extest2.c編程
c文件頭必須包含"Python.h"頭,以調用接口函數bash
這裏面寫了兩個c函數,模塊名稱定爲Extestide
#include "Python.h" #include <stdio.h> #include <stdlib.h> #include <string.h> int fac(int n) { if (n<2) return(1); return (n)*fac(n-1); } char *reverse(char *s) { register char t, *p = s, *q = (s + (strlen(s)-1)); while (p < q) { t = *p; *p++ = *q; *q-- = t; } return s; }
對這兩個函數採起C對Python API封裝,函數
static PyObject* 模塊名稱_C函數名稱(PyObject *self, PyObject *args)
PyArg_ParseTuple(args, "i", &num) //PyObject參數,類型指定,C參數存放地址1,[C參數存放地址2……]
(PyObject*)Py_BuildValue("ss", orig_str, \ dupe_str = reverse(strdup(orig_str))); //類型指定,C參數1,[C參數2……]
static PyObject* Extest_fac(PyObject *self, PyObject *args) { int num; if (!PyArg_ParseTuple(args, "i", &num)) return NULL; return (PyObject*)Py_BuildValue("i", fac(num)); } static PyObject* Extest_doppel(PyObject *self, PyObject *args) { char *orig_str; char *dupe_str; PyObject* retval; if (!PyArg_ParseTuple(args, "s", &orig_str)) return NULL; retval = (PyObject*)Py_BuildValue("ss", orig_str, \ dupe_str = reverse(strdup(orig_str))); free(dupe_str); return retval; }
首先,在擴展模塊中,你寫的函數都是像下面這樣的一個普通原型:工具
static PyObject *py_func(PyObject *self, PyObject *args) { ... }
PyObject
是一個能表示任何Python對象的C數據類型。 在一個高級層面,一個擴展函數就是一個接受一個Python對象 (在 PyObject *args中)元組並返回一個新Python對象的C函數。 函數的 self
參數對於簡單的擴展函數沒有被使用到, 不過若是你想定義新的類或者是C中的對象類型的話就能派上用場了。好比若是擴展函數是一個類的一個方法, 那麼 self
就能引用那個實例了。測試
PyArg_ParseTuple()
函數被用來將Python中的值轉換成C中對應表示。 它接受一個指定輸入格式的格式化字符串做爲輸入,好比「i」表明整數,「d」表明雙精度浮點數, 一樣還有存放轉換後結果的C變量的地址。 若是輸入的值不匹配這個格式化字符串,就會拋出一個異常並返回一個NULL值。 經過檢查並返回NULL,一個合適的異常會在調用代碼中被拋出。ui
Py_BuildValue()
函數被用來根據C數據類型建立Python對象。 它一樣接受一個格式化字符串來指按期望類型。 在擴展函數中,它被用來返回結果給Python。 Py_BuildValue()
的一個特性是它能構建更加複雜的對象類型,好比元組和字典。 在 py_divide()
代碼中,一個例子演示了怎樣返回一個元組。不過,下面還有一些實例:spa
return Py_BuildValue("i", 34); // Return an integer return Py_BuildValue("d", 3.4); // Return a double return Py_BuildValue("s", "Hello"); // Null-terminated UTF-8 string return Py_BuildValue("(ii)", 3, 4); // Tuple (3, 4)
庫信息以及初始化信息
/* 記錄函數信息,{函數在Python中名稱,函數對應封裝,參數格式(此處表示參數以元組格式傳入)} */ static PyMethodDef ExtestMethods[] = { {"fac", Extest_fac, METH_VARARGS}, {"doppel", Extest_doppel, METH_VARARGS}, {NULL, NULL}, }; /* Module structure */ static struct PyModuleDef Extestmodule = { PyModuleDef_HEAD_INIT, "Extest", /* 庫名稱 */ "A sample module", /* Doc string (may be NULL) */ -1, /* Size of per-interpreter state or -1 */ ExtestMethods /* 函數信息 */ }; /* Module initialization function */ PyMODINIT_FUNC PyInit_Extest(void) { /* PyInit_庫名稱 */ return PyModule_Create(&Extestmodule); /* 參數爲Module structure名詞 */ }
在擴展模塊底部,你會發現一個函數表,好比本節中的 ExtestMethods 表。 這個表能夠列出C函數、Python中使用的名字、文檔字符串。 全部模塊都須要指定這個表,由於它在模塊初始化時要被使用到。
最後的函數 PyInit_Extest()
是模塊初始化函數,但該模塊第一次被導入時執行。 這個函數的主要工做是在解釋器中註冊模塊對象。
from distutils.core import setup, Extension MOD = "Extest" setup(name=MOD, # 一個名字參數表示要編譯哪一個東西
ext_modules=[ # 一個list對象列出編譯對象
Extension(MOD, sources=['Extest2.c']) # 完整擴展名(可能含有.),源文件
])
調用:
python setup.py build python setup.py install
測試: