Python 是一門功能強大的腳本語言,它的強大不僅表現在功能上,還表現在其擴展性上。她提供大量的API以方便程序員利用C/C++對Python進行擴展。因爲 執行速度慢幾乎是所有腳本語言的通病,Python通過一個巧妙的方法,使得程序員可以利用C/C++編寫執行速度比較慢的模塊,從而獲得與C/C++差 不多的執行性能。本文給出一個例子說明怎樣用C來擴展Python。
1、在C和Python之間進行數據類型轉化
Python有六種基本數據類型:整型,浮點型,字符串,列表,字典,元組。在進行介紹之前,我們先了解一下怎麼在C和Python之間轉化幾種常用的數據類型。
整型轉化
PyObject *pInt = Py_BuildValue("i", 2007);
浮點型轉化
PyObject *pFloat = Py_BuildValue("f", 3.14);
字符串轉化
PyObject *pString = Py_BuildValue("s", "I am Yan.Dingcheng");
2、用C擴展Python
在瞭解了常用的數據類型轉化以後,我們就可以編寫一些C程序來擴展Python了。比如下面有個C文件exam.c,我們要在Python裏調用它裏面的函數。
int power(int a)
{
return a * a;
}
int add_int(int a, int b)
{
return a + b;
}
void my_print(const char *s)
{
if (!s) {
return;
}
printf("%s /n", s);
}
void whoami(void)
{
printf("I am plusboy. /n");
}
那麼我們首先要將其實現爲Python的一個模塊,這需要編寫一個封裝接口,我們把它保存爲wrap_exam.c,示例如下:
#include
PyObject *wrap_power(PyObject *self, PyObject *args)
{
int result, n;
/*這裏args包含Python解釋器要傳遞給C函數的參數列表,
"i:power"表示要給power函數傳遞一個整數,
n保存從Python腳本里傳過來的參數*/
if (!PyArg_ParseTuple(args, "i:power", &n)) {
return NULL;
}
result = power(n);
return Py_BuildValue("i", result);
}
PyObject *wrap_add_int(PyObject *self, PyObject *args)
{
int result, a, b;
/*這裏args包含Python解釋器要傳遞給C函數的參數列表,
"ii:power"表示要給add_int函數傳遞兩個整數,
a, b保存從Python腳本里傳過來的參數*/
if (!PyArg_ParseTuple(args, "ii:add_int", &a, &b)) {
return NULL;
}
result = add_int(a, b);
eturn Py_BuildValue("i", result);
}
PyObject *wrap_my_print(PyObject *self, PyObject *args)
{
char *s = NULL;
/*這裏args包含Python解釋器要傳遞給C函數的參數列表,
"s:my_print"表示要給my_print函數傳遞一個字符串,
s保存從Python腳本里傳過來的參數*/
if (!PyArg_ParseTuple(args, "s:my_print", &s)) {
Py_INCREF(Py_None);
return Py_None;
}
my_print(s);
Py_INCREF(Py_None);
return Py_None;
}
PyObject *wrap_whoami(PyObject *self, PyObject *args)
{
whoami();
Py_INCREF(Py_None); /*引用計數加1*/
return Py_None;
}
/*這裏一個函數列表,它給出了所有可以被Python使用的函數。
函數列表由四個部分組成:函數名,導出函數,參數傳遞方式和函數描述。
其中參數傳遞方式有兩種,METH_VARARGS是標準形式,它通過Python的元組
在Python解釋器和C函數之間傳遞參數,另一種是METH_KEYWORDS,它通過
Python的字典類型在Python解釋器和C函數之間傳遞參數。*/
static PyMethodDef examMethods[] = {
{"power", wrap_power, METH_VARARGS, "calculate power"},
{"add_int", wrap_add_int, METH_VARARGS, "add int"},
{"my_print", wrap_my_print, METH_VARARGS, "print string"},
{"whoami", wrap_whoami, METH_VARARGS, "who am i"},
{NULL, NULL, 0, NULL}
};
/*這是初始化函數,所有的擴展模塊都必須要有一個初始化函數,以便Python能對模塊進行初始化,
Python解釋器規定所有初始化函數的函數名都必須以以下方式構成:
init + 模塊名
這樣當Python解釋器導入模塊的時候將根據模塊名查找初始化函數,一旦找到就調用該初始化函數,
初始化函數則調用Py_InitModule()來註冊在該模塊中可以用到的函數。*/
void initexam()
{
PyObject *m = Py_InitModule("exam", examMethods);
}
最後是編譯鏈接
[plusboy@plusboy]$gcc -fpic -shared -o exam.so exam.c wrap_exam.c -I/usr/include/python2.3
下面是相應的Python測試程序,test.py
#!/usr/bin/python
import exam
print exam.power(2)
print exam.add_int(100, 100)
exam.my_print("I am plusboy")
exam.whoami()
3、執行結果
[plusboy@plusboy exam]$./test.py
4
200
I am plusboy
I am plusboy.
4、總結
所有的Python導出函數都有一個相同的函數原型
PyObject *fucn(PyObject *self, PyObject *args);
self參數只有當C函數被實現爲內聯函數的時候纔會被用到,args參數包含Python解釋器要傳遞給C函數的參數列表。
所有的導出函數都返回一個指向PyObject的指針,如果對應的C函數沒有返回值,即返回類型爲void,則應返回一個全局的None對像,並將其引用計數加1。如下所示:
PyObject *wrap_whoami(PyObject *self, PyObject *args)
{
whoami();
Py_INCREF(Py_None); /*引用計數加1*/
return Py_None;
}
用C/C++擴展Python語言
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.