原文地址:[http://www.isnowfy.com/introduction-to-python-c-extension/]
java
python是一門很是方便的動態語言,不少你用c或者java要不少行的代碼,可能python幾行就搞定了,因此python社區一直有個口號 「人生苦短,我用python」,可是方便至於,也帶來速度上的問題。python最被人詬病的就是程序的運行速度了,因此結合c的快速和python的 方便,就誕生了不少解決方案。首先注意到python就是c寫成的,因此最根本的解決方案就是利用原生的python c api來寫c程序,而後編譯成連接庫文件(linux下就是so文件),而後在python中直接調用,並且其餘的解決方案也基本是圍繞這個思路,只不過 替你作了不少重複的工做。此次主要是簡要介紹下python c api,swig,sip,ctypes,cython,cffi的使用。
python
首先來看最原始的就是使用python c api了。linux
#include <Python.h>api
static PyObject* add(PyObject* self, PyObject* args){app
int a = 0;python2.7
int b = 0;函數
if(!PyArg_ParseTuple(args, "i|i", &a, &b))ui
return NULL;spa
return Py_BuildValue("i", a+b);rest
}
static PyObject* sub(PyObject* self, PyObject* args){
int a = 0;
int b = 0;
if(!PyArg_ParseTuple(args, "i|i", &a, &b))
return NULL;
return Py_BuildValue("i", a-b);
}
static PyMethodDef addMethods[]={
{"add", add, METH_VARARGS},
{"sub", sub, METH_VARARGS},
{NULL, NULL, 0, NULL}
};
void initmytest(){
Py_InitModule("mytest", addMethods);
}
首先是引入Python.h這個頭文件,因此編譯的時候要注意引入python的庫,python中的對象在c中都是用PyObject來表示的, 程序中定義了add和sub兩個方法,而後編寫init函數,名字注意是init加上你的module的名字,而後調用Py_InitModule函數來 告訴python你定義的函數有哪些。而後就是把他編譯成so文件。
gcc mytest.c -shared -lpython2.7 -L /usr/lib/python2.7/ -I /usr/include/python2.7/ -o mytest.so
這樣你就能夠在python中import mytest這樣引用,用法就和用其餘python的模塊同樣了。
首先要說明的是swig能夠進行不少語言的調用轉換,不止是可讓python調用c。swig和sip都被稱做wrapper,就是說他對你的原 有函數進行了包裝。看到以前用python c api的方式裏,咱們必須嚴格按照python c api的方式來寫代碼,破壞了原有c程序的可讀性,因而wrapper的思想就是把原生c程序包裝成python c api那種方式的代碼,再去生成so文件。所以咱們要作的是首先寫c文件。
int add(int a, int b){
return a+b;
}
int sub(int a, int b){
return a-b;
}
而後再去寫一個swig格式的接口文件。
%module mytest
%{
extern int add(int a, int b);
extern int sub(int a, int b);
%}
extern int add(int a, int b);
extern int sub(int a, int b);
而後就能夠運行swig,他會自動生成python c api寫的代碼,而且會自動編譯出so文件來調用。
來看sip,sip是swig發展而來是方便python調用c的,因此基本使用方式都是差很少,只不過接口文件略有差別。
%Module(name=mytest, language="C")
int add(int a, int b);
int sub(int a, int b);
ctypes提供了另外的思路來調用c程序。首先ctypes是python的標準庫,因此若是用ctypes你不須要額外的其餘的東西。 ctypes讓你能夠在python直接寫代碼加載c的連接庫so文件來調用,就是說若是你用so文件而沒有源文件的話,你仍然能夠用ctypes去調 用。
from ctypes import *
f = 'mytest.so'
cdll.LoadLibrary(f)
api = CDLL(f)
api.add.argtypes = [c_int, c_int]
api.add.restype = c_int
api.sub.argtypes = [c_int, c_int]
api.sub.restype = c_int
print api.add(3, 2)
print api.sub(3, 2)
有點像在python中去寫接口文件,因爲是python的標準庫,因此這種方式用的仍是蠻多的。
cython的方法呢是利用相似python的語法來寫調用c程序的接口,而且能夠同時方便的地用c函數和python函數。看代碼理解。
int sub(int a, int b){
return a-b;
}
咱們能夠有一些c寫成的代碼。
cdef extern from 'test.c':
int sub(int a, int b)
def add(int a, int b):
return a+b
def mysub(a, b):
return sub(a, b)
而後在cython中咱們既能夠引入c文件調用c文件中的函數,也能夠去調用python中的函數,調用cython程序會把他變成純正的c文件,而後編譯成so文件就可使用了。
from cffi import FFI ffi = FFI() ffi.cdef(""" int add(int a, int b); int sub(int a, int b); """) lib = ffi.verify('#include "mytest.c"') print lib.add(1,2)
最後是cffi,cffi相似於ctypes直接在python程序中調用c程序,可是比ctypes更方便不要求編譯成so再調用,注意到上面的 全部方式都是須要去編譯成so文件後再在python中調用,而cffi容許你直接調用c文件來使用裏面的函數了,爲何這麼神奇呢,實際上是cffi在解 釋過程當中才幫你把c編譯爲so文件的。。。
而後基本就是這樣了,最後給個人感受就是:基本上原生的python c api的寫法最麻煩了,可是一些須要高級用法的話仍是這個更容易控制;方便一點的就是用wrapper;ctypes好處是,他是python的標準模塊 而且不須要另外寫其餘的額外程序(接口程序之類的);cython好處就是能夠方便的同時調用c函數和python函數,而且是類python語法,用起 來很方便;CFFI好處是調用c更加方便,不用編譯 so。最後本文只是對各類用法簡單的介紹,並無深刻的對各類用法的優缺點進行比較,所以若是想了解更多內容仍是去看官方文檔吧。。。