用C/C++擴展Python語言

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;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章