Python語言最初是用C語言實現的一種腳本語言,後來被稱爲CPython,是由於後來又有其它語言實現的Python,好比Python實現的Python——PyPy,Java語言實現的Python——Jython,.Net實現的Python——IronPython。linux
CPython具備優良的開放性和可擴展性,並提供了方便靈活的應用程序接口(API),從而使得C/C++程序員可以在各個級別上對Python解釋器的功能進行擴展。程序員
Python的C語言接口很適合封裝C語言實現的各類函數,若是要封裝C++的類,使用boost_python或者SWIG更方便和合適。python爬蟲
假設咱們有一個C函數:python2.7
/* 文件名: mylib.c */ int addone(int a) { return a+1; }
若是想在Python解釋器中調用該函數,則應該首先將其實現爲Python中的一個模塊,這須要編寫相應的封裝接口,以下所示:函數
/* wrap_mylib.c */ #include <Python.h> #include "mylib.h" PyObject* wrap_addone(PyObject* self, PyObject* args) { int n, result; if (! PyArg_ParseTuple(args, "i:fact", &n)) return NULL; result = addone(n); /*這裏調用C函數 */ return Py_BuildValue("i", result); } static PyMethodDef mylibMethods[] = { {"addone", wrap_addone, METH_VARARGS, "Add one to N"}, {NULL, NULL} }; void initmylib() { PyObject* m; m = Py_InitModule("mylib", mylibMethods); }
上面就是一個典型的Python擴展模塊,它至少應該包含三個部分:ui
要在Python解釋器中調用C語言中的某個函數,首先要爲它編寫對應的導出函數,上述例子中的導出函數爲wrap_addone。在Python的C語言擴展中,全部的導出函數都具備相同的函數原型:spa
PyObject* wrap_method(PyObject* self, PyObject* args);
這個函數是Python解釋器和C函數進行交互的接口,通常以wrap_開頭後面跟上C語言的函數名,這樣命名把導出函數和C語言函數對應起來使得代碼更加清晰。它帶有兩個參數:self和args。指針
參數self 只在C函數被實現爲內聯方法(built-in method)時才被用到,一般該參數的值爲空(NULL)。
參數args 中包含了Python解釋器要傳遞給C函數的全部參數,一般使用Python的C語言擴展接口提供的函數PyArg_ParseTuple()來得到這些參數值。code
全部的導出函數都返回一個PyObject指針,若是對應的C函數沒有真正的返回值(即返回值類型爲void),則應返回一個全局的None對象(Py_None),並將其引用計數增1,以下所示:
PyObject* wrap_method(PyObject *self, PyObject *args) { Py_INCREF(Py_None); return Py_None; }
方法列表中列出了全部能夠被Python解釋器使用的方法,上述例子對應的方法列表爲:
static PyMethodDef mylibMethods[] = { {"addone", wrap_addone, METH_VARARGS, "Add one to N"}, {NULL, NULL} };
方法列表中的每項由四個部分組成:
方法名是從Python解釋器中調用該方法時所使用的名字。
參數傳遞方式則規定了Python向C函數傳遞參數的具體形式,可選的兩種方式是METH_VARARGS和METH_KEYWORDS,其中METH_VARARGS是參數傳遞的標準形式,它經過Python的元組在Python解釋器和C函數之間傳遞參數,若採用METH_KEYWORD方式,則Python解釋器和C函數之間將經過Python的字典類型在二者之間進行參數傳遞。
全部的Python擴展模塊都必需要有一個初始化函數,以便Python解釋器可以對模塊進行正確的初始化。Python解釋器規定全部的初始化函數的函數名都必須以init開頭,並加上模塊的名字。對於模塊mylib來講,則相應的初始化函數爲:
void initmylib() { PyObject* m; m = Py_InitModule("mylib", mylibMethods); }
當Python解釋器須要導入該模塊時,將根據該模塊的名稱查找相應的初始化函數,一旦找到則調用該函數進行相應的初始化工做,初始化函數則經過調用Python的C語言擴展接口所提供的函數Py_InitModule(),來向Python解釋器註冊該模塊中全部能夠用到的方法。
要在Python解釋器中使用C語言編寫的擴展模塊,必須將其編譯成動態連接庫的形式。下面以Linux爲例,介紹如何將C編寫的Python擴展模塊編譯成動態連接庫:
$ gcc -fpic -shared -o mylib.so \ -I/usr/include/python2.7 \ mylib.c wrap_mylib.c
上面編譯生成的Python擴展模塊的動態連接庫,能夠在Python中直接import。以下所示:
veelion@gtx:~$ python Python 2.7.12 (default, Nov 19 2016, 06:48:10) [GCC 5.4.0 20160609] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> import example >>> example.addone(7) 8 >>>
這裏生成的.so動態庫和上一篇中不用Python的C語言生成的動態庫是不同的,從生成過程和使用方法就能夠看出來,這裏的動態庫使用起來感受就是一個Python模塊,直接import就能夠了。