開發環境:Ubuntu9.10,python2.6,gcc4.4.1
1,ubuntu下的python運作包和開發包是分開的,是以需要在新利得裡面安裝python-all-dev,進而可以在代碼中引用python的頭檔案和庫。
2.下面是一個最簡單的可以供python調用的c擴充子產品,假設c程式檔案名為foo.c:
複制代碼
代碼
#include <Python.h>
static PyObject* foo_bar(PyObject* self, PyObject* args) {
Py_RETURN_NONE;
}
static PyMethodDef foo_methods[] = {
{"bar",(PyCFunction)foo_bar,METH_NOARGS,NULL},
{NULL,NULL,0,NULL}
};
PyMODINIT_FUNC initfoo() {
Py_InitModule3("foo", foo_methods, "My first extension module.");
}
我們可以将上述子產品分成3個部分:1)c子產品想對外暴露的接口函數。2)提供給外部的python程式使用的一個c子產品函數名稱映射表。3)c子產品的初始化函數。子產品的第一行将Python.h引入到子產品中,這個檔案将使得你的子產品可以hook進python的解釋器,進而可以為外部的python程式所使用。
c子產品中的函數簽名一般有下列三種形式:
PyObject* MyFunction(PyObject* self, PyObject* args);
PyObject* MyFunctionWithKeywords(PyObject* self, PyObject* args, PyObject* kw);
PyObject* MyFunctionWithNoArgs(PyObject* self);
一般我們使用的是第一種方式,函數的參數将會一個元組(tuple)的形式傳進來,是以我們在c子產品的函數中需要對其進行解析。Python中不能象c語言一樣聲明一個void類型的函數,如果你不想函數傳回一個值的話,那就傳回一個NONE,在這裡我們可以通過Python頭檔案中的一個宏Py_RETURN_NONE來實作。
C子產品中的函數名稱其實對外部來說是不可見的,是以可以随便你命名,一般我們可以使用static函數(這在C語言裡表示在目前檔案以外是不可見的)。本文函數命名方式采用子產品名加上函數名,例如foo_bar,這表示在子產品foo中會有一個bar函數。然後就是函數映射表了,它是一個PyMethodDef結構體數組,
struct PyMethodDef {
char* ml_name;
PyCFunction ml_meth;
int ml_flags;
char* ml_doc;
第一個成員ml_name是函數名,當我們在外部的Python代碼中使用此子產品時利用這個名稱進行函數調用。ml_meth是函數位址。ml_flags告訴解釋器ml_meth将會使用上述三種方法簽名的哪一種,一般設定為METH_VARARGS,如果你想允許關鍵字參數,則可以将其與METH_KEYWORDS進行或運算。若不想接受任何參數,則可以将其設定為METH_NOARGS.最後,ml_doc字段是函數的注釋文檔資訊,最好還是寫幾句吧,不然會被鄙視的。。。另外,這個表必須以{NULL,NULL,0,NULL}這樣一條空記錄結尾。
子產品的初始化函數是在子產品被加載時被Python解釋器所調用的,如果你的子產品名為foo,則要求命名為initfoo.Py_InitModule3函數一般用來定義一個子產品。
3,現在我們來将foo.c檔案編譯為一個擴充子產品,使用下述指令進行編譯:
gcc -shared -I /usr/include/python2.6 foo.c -o foo.so
注意shared object的名稱必須和傳給Py_InitModule3函數的字元串一緻,另一種可選的方式是加上module字尾,是以上述foo子產品可以命名為foo.so或foomodule.so。
4,上面的編譯方式可以完成任務,但更好的生成擴充子產品的方法是使用distutils。首先寫一個setup.py腳本:
from distutils.core import setup, Extension
setup(name = 'foo', version = '1.0', ext_modules = [Extension('foo', ['foo.c'])])
然後執行下述指令進行build:
python ./setup.py build
這會在目前目錄下生成一個build子目錄,其中包含了中間生成的foo.o以及最後生成出來的foo.so。當然,最簡單的方法是使用下述指令進行子產品的生成和安裝:
python ./setup.py install
注:由于需要獲得dist-packages的寫權限,最好先切換到root使用者,如果直接使用su切換出現下面的錯誤:
su: Authentication failure
則為root使用者設定一個新密碼:
sudo passwd root
再用新密碼切換到root使用者。檢視build時的詳細情況,我們可以發現這麼一句:
copying build/lib.linux-i686-2.6/foo.so -> /usr/local/lib/python2.6/dist-packages
這是将生成的子產品拷貝到/usr/local/lib/python2.6/dist-packages下了,這樣就将我們的foo子產品安裝到系統中了,我們可以驗證如下,在python指令行中,
import foo
dir(foo)
結果如下:
['__doc__','__file__','__name__','__package__','bar']
呵呵,不錯吧,這個foo子產品現在已經和其他系統子產品一樣了,原因就在于dist-packages是在sys.path這個路徑中的,
5,現在我們手上已經有一個生成并安裝好的C擴充子產品了,剩下的就是在python代碼中引入這個新子產品,并調用它的方法
foo.bar()
當然,由于在c子產品中的bar函數裡,我們目前什麼都還沒做,是以現在啥都沒有,在下一篇中我們實作:1)從python腳本裡向C子產品中傳遞參數。2)從C子產品中傳回值給外部的Python腳本
夜已經深了,這個python和c/c++,java相結合系列的第一篇就暫時寫到這裡。。。
本文轉自Phinecos(洞庭散人)部落格園部落格,原文連結:http://www.cnblogs.com/phinecos/archive/2010/05/17/1737033.html,如需轉載請自行聯系原作者