1. 程式人生 > >用C語言擴充套件Python的功能

用C語言擴充套件Python的功能

一、簡介

Python是一門功能強大的高階指令碼語言,它的強大不僅表現在其自身的功能上,而且還表現在其良好的可擴充套件性上,正因如此,Python已經開始受到越來越多人的青睞,並且被屢屢成功地應用於各類大型軟體系統的開發過程中。

與其它普通指令碼語言有所不同,Python程式設計師可以藉助Python語言提供的API,使用C或者C++來對Python進行功能性擴充套件,從而即可以利用Python方便靈活的語法和功能,又可以獲得與C或者C++幾乎相同的執行效能。執行速度慢是幾乎所有指令碼語言都具有的共性,也是倍受人們指責的一個重要因素,Python則通過與C語言的有機結合巧妙地解決了這一問題,從而使指令碼語言的應用範圍得到了很大擴充套件。

在用Python開發實際軟體系統時,很多時候都需要使用C/C++來對Python進行擴充套件。最常見的情況是目前已經存在一個用C編寫的庫,需要在Python語言中使用該庫的某些功能,此時就可以藉助Python提供的擴充套件功能來實現。此外,由於Python從本質上講還是一種指令碼語言,某些功能用Python實現可能很難滿足實際軟體系統對執行效率的要求,此時也可以藉助Python提供的擴充套件功能,將這些關鍵程式碼段用C或者C++實現,從而提供程式的執行效能。

本文主要介紹Python提供的C語言擴充套件介面,以及如何使用這些介面和C/C++語言來對Python進行功能性擴充套件,並輔以具體的例項講述如何實現Python的功能擴充套件。

二、Python的C語言介面

Python是用C語言實現的一種指令碼語言,本身具有優良的開放性和可擴充套件性,並提供了方便靈活的應用程式介面(API),從而使得C/C++程式設計師能夠在各個級別上對Python直譯器的功能進行擴充套件。在使用C/C++對Python進行功能擴充套件之前,必須首先掌握Python解釋所提供的C語言介面。

2.1 Python物件(PyObject)

Python是一門面向物件的指令碼語言,所有的物件在Python直譯器中都被表示成PyObject,PyObject結構包含Python物件的所有成員指標,並且對Python物件的型別資訊和引用計數進行維護。在進行Python的擴充套件程式設計時,一旦要在C或者C++中對Python物件進行處理,就意味著要維護一個PyObject結構。

在Python的C語言擴充套件介面中,大部分函式都有一個或者多個引數為PyObject指標型別,並且返回值也大都為PyObject指標。

2.2 引用計數

為了簡化記憶體管理,Python通過引用計數機制實現了自動的垃圾回收功能,Python中的每個物件都有一個引用計數,用來計數該物件在不同場所分別被引用了多少次。每當引用一次Python物件,相應的引用計數就增1,每當消毀一次Python物件,則相應的引用就減1,只有當引用計數為零時,才真正從記憶體中刪除Python物件。

下面的例子說明了Python直譯器如何利用引用計數來對Pyhon物件進行管理:

例1:refcount.py
class refcount:
    # etc.
r1 = refcount() # 引用計數為1
r2 = r1         # 引用計數為2
del(r1)         # 引用計數為1
del(r2)         # 引用計數為0,刪除物件

在C/C++中處理Python物件時,對引用計數進行正確的維護是一個關鍵問題,處理不好將很容易產生記憶體洩漏。Python的C語言介面提供了一些巨集來對引用計數進行維護,最常見的是用Py_INCREF()來增加使Python物件的引用計數增1,用Py_DECREF()來使Python物件的引用計數減1。

2.3 資料型別

Python定義了六種資料型別:整型、浮點型、字串、元組、列表和字典,在使用C語言對Python進行功能擴充套件時,首先要了解如何在C和Python的資料型別間進行轉化。

2.3.1 整型、浮點型和字串

在Python的C語言擴充套件中要用到整型、浮點型和字串這三種資料型別時相對比較簡單,只需要知道如何生成和維護它們就可以了。下面的例子給出瞭如何在C語言中使用Python的這三種資料型別:

例2:typeifs.c
// build an integer
PyObject* pInt = Py_BuildValue("i", 2003);
assert(PyInt_Check(pInt));
int i = PyInt_AsLong(pInt);
Py_DECREF(pInt);
// build a float
PyObject* pFloat = Py_BuildValue("f", 3.14f);
assert(PyFloat_Check(pFloat));
float f = PyFloat_AsDouble(pFloat);
Py_DECREF(pFloat);
// build a string
PyObject* pString = Py_BuildValue("s", "Python");
assert(PyString_Check(pString);
int nLen = PyString_Size(pString);
char* s = PyString_AsString(pString);
Py_DECREF(pString);

2.3.2 元組

Python語言中的元組是一個長度固定的陣列,當Python直譯器呼叫C語言擴充套件中的方法時,所有非關鍵字(non-keyword)引數都以元組方式進行傳遞。下面的例子示範瞭如何在C語言中使用Python的元組型別:

例3:typetuple.c
// create the tuple
PyObject* pTuple = PyTuple_New(3);
assert(PyTuple_Check(pTuple));
assert(PyTuple_Size(pTuple) == 3);
// set the item
PyTuple_SetItem(pTuple, 0, Py_BuildValue("i", 2003));
PyTuple_SetItem(pTuple, 1, Py_BuildValue("f", 3.14f));
PyTuple_SetItem(pTuple, 2, Py_BuildValue("s", "Python"));
// parse tuple items
int i;
float f;
char *s;
if (!PyArg_ParseTuple(pTuple, "ifs", &i, &f, &s))
    PyErr_SetString(PyExc_TypeError, "invalid parameter");
// cleanup
Py_DECREF(pTuple);

2.3.3 列表

Python語言中的列表是一個長度可變的陣列,列表比元組更為靈活,使用列表可以對其儲存的Python物件進行隨機訪問。下面的例子示範瞭如何在C語言中使用Python的列表型別:

例4:typelist.c
// create the list
PyObject* pList = PyList_New(3); // new reference
assert(PyList_Check(pList));
// set some initial values
for(int i = 0; i < 3; ++i)
    PyList_SetItem(pList, i, Py_BuildValue("i", i));
// insert an item
PyList_Insert(pList, 2, Py_BuildValue("s", "inserted"));
// append an item
PyList_Append(pList, Py_BuildValue("s", "appended"));
// sort the list
PyList_Sort(pList);
// reverse the list
PyList_Reverse(pList);
// fetch and manipulate a list slice
PyObject* pSlice = PyList_GetSlice(pList, 2, 4); // new reference
for(int j = 0; j < PyList_Size(pSlice); ++j) {
  PyObject *pValue = PyList_GetItem(pList, j);
  assert(pValue);
}
Py_DECREF(pSlice);
// cleanup
Py_DECREF(pList);

2.3.4 字典

Python語言中的字典是一個根據關鍵字進行訪問的資料型別。下面的例子示範瞭如何在C語言中使用Python的字典型別:

例5:typedic.c
// create the dictionary
PyObject* pDict = PyDict_New(); // new reference
assert(PyDict_Check(pDict));
// add a few named values
PyDict_SetItemString(pDict, "first", 
                     Py_BuildValue("i", 2003));
PyDict_SetItemString(pDict, "second", 
                     Py_BuildValue("f", 3.14f));
// enumerate all named values
PyObject* pKeys = PyDict_Keys(); // new reference
for(int i = 0; i < PyList_Size(pKeys); ++i) {
  PyObject *pKey = PyList_GetItem(pKeys, i);
  PyObject *pValue = PyDict_GetItem(pDict, pKey);
  assert(pValue);
}
Py_DECREF(pKeys);
// remove a named value
PyDict_DelItemString(pDict, "second");
// cleanup
Py_DECREF(pDict);

三、Python的C語言擴充套件

3.1 模組封裝

在瞭解了Python的C語言介面後,就可以利用Python直譯器提供的這些介面來編寫Python的C語言擴充套件,假設有如下一個C語言函式:

例6:example.c
int fact(int n)
{
  if (n <= 1) 
    return 1;
  else 
    return n * fact(n - 1);
}

該函式的功能是計算某個給定自然數的階乘,如果想在Python直譯器中呼叫該函式,則應該首先將其實現為Python中的一個模組,這需要編寫相應的封裝介面,如下所示:

例7: wrap.c
#include <Python.h>
PyObject* wrap_fact(PyObject* self, PyObject* args) 
{
  int n, result;
  
  if (! PyArg_ParseTuple(args, "i:fact", &n))
    return NULL;
  result = fact(n);
  return Py_BuildValue("i", result);
}
static PyMethodDef exampleMethods[] = 
{
  {"fact", wrap_fact, METH_VARARGS, "Caculate N!"},
  {NULL, NULL}
};
void initexample() 
{
  PyObject* m;
  m = Py_InitModule("example", exampleMethods);
}

一個典型的Python擴充套件模組至少應該包含三個部分:匯出函式、方法列表和初始化函式。

3.2 匯出函式

要在Python直譯器中使用C語言中的某個函式,首先要為其編寫相應的匯出函式,上述例子中的匯出函式為wrap_fact。在Python的C語言擴充套件中,所有的匯出函式都具有相同的函式原型:

PyObject* method(PyObject* self, PyObject* args);

該函式是Python直譯器和C函式進行互動的介面,帶有兩個引數:self和args。引數self只在C函式被實現為內聯方法(built-in method)時才被用到,通常該引數的值為空(NULL)。引數args中包含了Python直譯器要傳遞給C函式的所有引數,通常使用Python的C語言擴充套件介面提供的函式PyArg_ParseTuple()來獲得這些引數值。

所有的匯出函式都返回一個PyObject指標,如果對應的C函式沒有真正的返回值(即返回值型別為void),則應返回一個全域性的None物件(Py_None),並將其引用計數增1,如下所示:

PyObject* method(PyObject *self, PyObject *args) 
{
  Py_INCREF(Py_None);
  return Py_None;
}

3.3 方法列表

方法列表中給出了所有可以被Python直譯器使用的方法,上述例子對應的方法列表為:

static PyMethodDef exampleMethods[] = 
{
  {"fact", wrap_fact, METH_VARARGS, "Caculate N!"},
  {NULL, NULL}
};

方法列表中的每項由四個部分組成:方法名、匯出函式、引數傳遞方式和方法描述。方法名是從Python直譯器中呼叫該方法時所使用的名字。引數傳遞方式則規定了Python向C函式傳遞引數的具體形式,可選的兩種方式是METH_VARARGS和METH_KEYWORDS,其中METH_VARARGS是引數傳遞的標準形式,它通過Python的元組在Python直譯器和C函式之間傳遞引數,若採用METH_KEYWORD方式,則Python直譯器和C函式之間將通過Python的字典型別在兩者之間進行引數傳遞。

3.4 初始化函式

所有的Python擴充套件模組都必須要有一個初始化函式,以便Python直譯器能夠對模組進行正確的初始化。Python直譯器規定所有的初始化函式的函式名都必須以init開頭,並加上模組的名字。對於模組example來說,則相應的初始化函式為:

void initexample() 
{
  PyObject* m;
  m = Py_InitModule("example", exampleMethods);
}

當Python直譯器需要匯入該模組時,將根據該模組的名稱查詢相應的初始化函式,一旦找到則呼叫該函式進行相應的初始化工作,初始化函式則通過呼叫Python的C語言擴充套件介面所提供的函式Py_InitModule(),來向Python直譯器註冊該模組中所有可以用到的方法。

3.5 編譯連結

要在Python直譯器中使用C語言編寫的擴充套件模組,必須將其編譯成動態連結庫的形式。下面以RedHat Linux 8.0為例,介紹如何將C編寫的Python擴充套件模組編譯成動態連結庫:

[[email protected] code]$ gcc -fpic -c -I/usr/include/python2.2 \
                    -I /usr/lib/python2.2/config \
                    example.c wrapper.c
[[email protected] code]$ gcc -shared -o example.so example.o wrapper.o

3.6 引入Python直譯器

當生成Python擴充套件模組的動態連結庫後,就可以在Python直譯器中使用該擴充套件模組了,與Python自帶的模組一樣,擴充套件模組也是通過import命令引入後再使用的,如下所示:

[[email protected] code]$ python
Python 2.2.1 (#1, Aug 30 2002, 12:15:30)
[GCC 3.2 20020822 (Red Hat Linux Rawhide 3.2-4)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import example
>>> example.fact(4)
24
>>>

四、結束語

作為一門功能強大的指令碼語言,Python將被更加廣泛地應用於各個領域。為了克服指令碼語言執行速度慢的問題,Python提供了相應的C語言擴充套件介面,通過將影響執行效能的關鍵程式碼用C語言實現,可以很大程度上提高用Python編寫的指令碼在執行時的速度,從而滿足實際需要。

相關推薦

C語言擴充套件Python功能

一、簡介 Python是一門功能強大的高階指令碼語言,它的強大不僅表現在其自身的功能上,而且還表現在其良好的可擴充套件性上,正因如此,Python已經開始受到越來越多人的青睞,並且被屢屢成功地應用於各類大型軟體系統的開發過程中。 與其它普通指令碼語言有所不同,Python程式設計師可以藉助Python語

C語言擴充套件Python

最近一直在跟隨《PYTHON核心程式設計》學習一些python的編寫,可惜的是這本書的版本太過於陳舊。大部分範例程式碼都是python2的版本。 剛剛在看python用C語言寫擴充套件包的時候踩到了一個大坑,到現在沒用爬上來 跟其他的python程式碼一樣,擴充套件包也無非就是調包而已,要把python的

C語言擴充套件Python介面

 #假設模組名字是FMBus,並且被儲存為fmbusmodule.c #include"Python.h" #include"structmember.h"    //Python\Python36\include  路徑下 #include"object.h"   /

c語言實現日誌功能

windows.h 用c語言實現 pause truct argc ifdef open info oid // 參考鏈接 // C語言實現寫入日誌文件 https://blog.csdn.net/sunlion81/article/details/8647028 #

C語言擴充套件lua模組(入門)

#include <lua.h> #include <lauxlib.h> #include <lualib.h> static void encode_sha(const char* src, char* des) { /* *sha-hash /

c語言實現日曆功能

#include <stdio.h> #include <windows.h> #pragma warning(disable:4996) void print(int week, int day) { int i = 0;

Python調C語言

word c函數 blog 現在 sum 生成 from str and Python中的ctypes模塊可能是Python調用C方法中最簡單的一種。ctypes模塊提供了和C語言兼容的數據類型和函數來加載dll文件,因此在調用時不需對源文件做任何的修改。也正是如此奠定了這

使用ctypes調c語言接口+使用virtualenv隔離python環境

ffffff pro 計算 virtual ima fly 隔離 語言 text 文章內容為原創,歡迎轉載請註明出處 作者: EflyPro->晦明禪師 文章來源:公眾號“睿江雲計算” 使用ctypes調用c語言接口+使用virtualenv隔離python環境

C語言實現掃雷部分功能(可連續展開)

用了用了兩天時間,寫了個掃雷的小程式,是不是有點慢了。不過我是真覺得用c寫小東西很有意思。 PS:重新遊戲那裡還沒開始寫,後面會補上。 ** 感覺最困難,花費時間最多的在棋盤中無雷周邊區域顯示的演算法上。 ** 具體程式見下: 標頭檔案 #ifndef __SAO

[領卓教育]C語言實現ls以及ls-功能

各位程式設計師在自己的虛擬機器裡一定沒少執行過“ls”這個功能吧,這個程式碼就是實現了ls和ls-l功能,話不多說,上程式碼。 實現程式碼 int process_ls(char * path) { DIR * dirp; struct dire

C語言寫一個數組,實現類似JAVA語言中ArrayList的功能

此程式是在看過郝斌老師的資料結構與演算法的視訊後,自己用C語言實現的。整個程式比較簡單,適合入門資料結構時練手。 #include <stdio.h> #include <stdlib.h> #include <malloc.h> #

C語言實現Ping程式功能(轉)

用C語言實現Ping程式功能 日期:2006-12-25 作者:樑俊輝 來自:IBM DW中國 大部分人用ping命令只是作為檢視另一個系統的網路連線是否正常的一種簡單方法。在這篇文章中,作者將介紹如何用C語言編寫一個模擬ping命令功能的程式。 ping命令是用來檢視網路

c語言實現 一個通訊錄(實現 增加、刪除、查詢、修改、顯示、清空功能

源程式標頭檔案contact.h #ifndef _CONTACT_H__ #define _CONTACT_H__ #define NAME_MAX 20 #define SEX_MAX 5 #define TELE_MAX 15 #define ADDR_MAX 5

C語言模擬實現一個通訊錄,要求實現其新增 刪除 修改 查詢 模糊搜尋姓名和號碼 顯示和排序聯絡人資訊的功能

                題目:用C語言模擬實現一個通訊錄,要求實現其新增、刪除、修改、查詢、模糊搜尋姓名和號碼、顯示和排序

c語言實現一個strcat函式功能相同的函式

extern char *strcat(char *dest,char *src); 把src所指字串新增到dest結尾處(覆蓋dest結尾處的'\0')並新增'\0'。 #include "std

中國有句俗語叫“三天打魚兩天晒網”。某人從2010年1月1日起開始“三天打魚兩天晒網”,問這個人在以後的某一天中是“打魚”還是“晒網”。CC++語言/java/python實現程式解決問題。

提高要求:1.輸入資料的正確性驗證。           2.使用檔案進行資料測試。如將日期 20100101   20111214  等資料儲存在in.txt檔案中,程式讀入in.dat檔案進行判定,並將結果輸出至out.txt檔案。 import java.io.Bu

C語言解決迷宮問題

type out reverse print top stdio.h || 當前位置 坐標 #include <stdio.h> #include <stdlib.h> #define ROW 10 #define COL 10 /*迷宮中位

c語言指針處理字符串

image tdi while clas int color result 處理 std 字符串的處理方法有兩種:一種方法是使用字符數組處理字符串,另一種是方法是使用字符指針處理字符串。 後一種也是c語言比較常用的方法。下面我們來看一個列子: 1 #include<

C語言讀寫數據

cor 數據 and main () term int blog abi //1-5題 #include "stdio.h" typedef struct { char name[10];//姓名 int subject1,subject2,subject3

Linux C語言判斷文件和文件夾

nis ces 文件 char int pat code col exist Linux 用C語言判斷文件和文件夾 #include <stdio.h> #include <stdlib.h> #include <unistd.h> #