午夜视频在线网站,日韩视频精品在线,中文字幕精品一区二区三区在线,在线播放精品,1024你懂我懂的旧版人,欧美日韩一级黄色片,一区二区三区在线观看视频

分享

Python實例淺談之三Python與C/C 相互調(diào)用

 advanced00 2018-10-06
復制代碼
復制代碼
#include <stdio.h> #include <stdlib.h> #include <string.h> int fac(int n) { if (n < 2) return(1); /* 0! == 1! == 1 */ return (n)*fac(n-1); /* n! == n*(n-1)! */ } char *reverse(char *s) { register char t, /* tmp */ *p = s, /* fwd */ *q = (s (strlen(s) - 1)); /* bwd */ while (p < q) /* if p < q */ { t = *p; /* swap & move ptrs */ *p = *q; *q-- = t; } return(s); } int main() { char s[BUFSIZ]; printf('4! == %d\n', fac(4)); printf('8! == %d\n', fac(8)); printf('12! == %d\n', fac(12)); strcpy(s, 'abcdef'); printf('reversing 'abcdef', we get '%s'\n', \ reverse(s)); strcpy(s, 'madam'); printf('reversing 'madam', we get '%s'\n', \ reverse(s)); return 0; }
復制代碼
復制代碼

       上述代碼中有兩個函數(shù),一個是遞歸求階乘的函數(shù)fac();另一個reverse()函數(shù)實現(xiàn)了一個簡單的字符串反轉(zhuǎn)算法,其主要目的是修改傳入的字符串,使其內(nèi)容完全反轉(zhuǎn),但不需要申請內(nèi)存后反著復制的方法。

(2)用樣板來包裝代碼
        接口的代碼被稱為“樣板”代碼,它是應用程序代碼與Python解釋器之間進行交互所必不可少的一部分。樣板主要分為4步:a、包含Python的頭文件;b、為每個模塊的每一個函數(shù)增加一個型如PyObject* Module_func()的包裝函數(shù);c、為每個模塊增加一個型如PyMethodDef ModuleMethods[]的數(shù)組;d、增加模塊初始化函數(shù)void initModule()。
復制代碼
復制代碼
#include <stdio.h> #include <stdlib.h> #include <string.h> int fac(int n) { if (n < 2) return(1); return (n)*fac(n-1); } char *reverse(char *s) { register char t, *p = s, *q = (s (strlen(s) - 1)); while (s && (p < q)) { t = *p; *p = *q; *q-- = t; } return(s); } int test() { char s[BUFSIZ]; printf('4! == %d\n', fac(4)); printf('8! == %d\n', fac(8)); printf('12! == %d\n', fac(12)); strcpy(s, 'abcdef'); printf('reversing 'abcdef', we get '%s'\n', \ reverse(s)); strcpy(s, 'madam'); printf('reversing 'madam', we get '%s'\n', \ reverse(s)); return 0; } #include 'Python.h' static PyObject * Extest_fac(PyObject *self, PyObject *args) { int num; if (!PyArg_ParseTuple(args, 'i', &num)) return NULL; return (PyObject*)Py_BuildValue('i', fac(num)); } static PyObject * Extest_doppel(PyObject *self, PyObject *args) { char *orig_str; char *dupe_str; PyObject* retval; if (!PyArg_ParseTuple(args, 's', &orig_str)) return NULL; retval = (PyObject*)Py_BuildValue('ss', orig_str, dupe_str=reverse(strdup(orig_str))); free(dupe_str); #防止內(nèi)存泄漏 return retval; } static PyObject * Extest_test(PyObject *self, PyObject *args) { test(); return (PyObject*)Py_BuildValue(''); } static PyMethodDef ExtestMethods[] = { { 'fac', Extest_fac, METH_VARARGS }, { 'doppel', Extest_doppel, METH_VARARGS }, { 'test', Extest_test, METH_VARARGS }, { NULL, NULL }, }; void initExtest() { Py_InitModule('Extest', ExtestMethods); }
復制代碼
復制代碼

        Python.h頭文件在大多數(shù)類Unix系統(tǒng)中會在/usr/local/include/python2.x或/usr/include/python2.x目錄中,系統(tǒng)一般都會知道文件安裝的路徑。

        增加包裝函數(shù),所在模塊名為Extest,那么創(chuàng)建一個包裝函數(shù)叫Extest_fac(),在Python腳本中使用是先import Extest,然后調(diào)用Extest.fac(),當Extest.fac()被調(diào)用時,包裝函數(shù)Extest_fac()會被調(diào)用,包裝函數(shù)接受一個 Python的整數(shù)參數(shù),把它轉(zhuǎn)為C的整數(shù),然后調(diào)用C的fac()函數(shù),得到一個整型的返回值,最后把這個返回值轉(zhuǎn)為Python的整型數(shù)做為整個函數(shù)調(diào)用的結(jié)果返回回去。其他兩個包裝函數(shù)Extest_doppel()和Extest_test()類似。
         從Python到C的轉(zhuǎn)換用PyArg_Parse*系列函數(shù),int PyArg_ParseTuple():把Python傳過來的參數(shù)轉(zhuǎn)為C;int PyArg_ParseTupleAndKeywords()與PyArg_ParseTuple()作用相同,但是同時解析關(guān)鍵字參數(shù);它們的用法跟C的sscanf函數(shù)很像,都接受一個字符串流,并根據(jù)一個指定的格式字符串進行解析,把結(jié)果放入到相應的指針所指的變量中去,它們的返回值為1表示解析成功,返回值為0表示失敗。從C到Python的轉(zhuǎn)換函數(shù)是PyObject* Py_BuildValue():把C的數(shù)據(jù)轉(zhuǎn)為Python的一個對象或一組對象,然后返回之;Py_BuildValue的用法跟sprintf很像,把所有的參數(shù)按格式字符串所指定的格式轉(zhuǎn)換成一個Python的對象。
        C與Python之間數(shù)據(jù)轉(zhuǎn)換的轉(zhuǎn)換代碼:

        為每個模塊增加一個型如PyMethodDef ModuleMethods[]的數(shù)組,以便于Python解釋器能夠?qū)氩⒄{(diào)用它們,每一個數(shù)組都包含了函數(shù)在Python中的名字,相應的包裝函數(shù)的名字以及一個METH_VARARGS常量,METH_VARARGS表示參數(shù)以tuple形式傳入。 若需要使用PyArg_ParseTupleAndKeywords()函數(shù)來分析命名參數(shù)的話,還需要讓這個標志常量與METH_KEYWORDS常量進行邏輯與運算常量 。數(shù)組最后用兩個NULL來表示函數(shù)信息列表的結(jié)束。
         所有工作的最后一部分就是模塊的初始化函數(shù),調(diào)用Py_InitModule()函數(shù),并把模塊名和ModuleMethods[]數(shù)組的名字傳遞進去,以便于解釋器能正確的調(diào)用模塊中的函數(shù)。
(3)編譯
        為了讓新Python的擴展能被創(chuàng)建,需要把它們與Python庫放在一起編譯,distutils包被用來編譯、安裝和分發(fā)這些模塊、擴展和包。
        創(chuàng)建一個setup.py 文件,編譯最主要的工作由setup()函數(shù)來完成:
復制代碼
#!/usr/bin/env python from distutils.core import setup, Extension MOD = 'Extest' setup(name=MOD, ext_modules=[Extension(MOD, sources=['Extest2.c'])])
復制代碼

        Extension()第一個參數(shù)是(完整的)擴展的名字,如果模塊是包的一部分的話,還要加上用'.'分隔的完整的包的名字。上述的擴展是獨立的,所以名字只要寫'Extest'就行;sources參數(shù)是所有源代碼的文件列表,只有一個文件Extest2.c。setup需要兩個參數(shù):一個名字參數(shù)表示要編譯哪個內(nèi)容;另一個列表參數(shù)列出要編譯的對象,上述要編譯的是一個擴展,故把ext_modules參數(shù)的值設(shè)為擴展模塊的列表。

        運行setup.py build命令就可以開始編譯我們的擴展了,提示部分信息:
creating build/lib.linux-x86_64-2.6 gcc -pthread -shared build/temp.linux-x86_64-2.6/Extest2.o -L/usr/lib64 -lpython2.6 -o build/lib.linux-x86_64-2.6/Extest.so

(4)導入和測試

         你的擴展會被創(chuàng)建在運行setup.py腳本所在目錄下的build/lib.*目錄中,可以切換到那個目錄中來測試模塊,或者也可以用命令把它安裝到Python中:python setup.py install,會提示相應信息。
         測試模塊:


(5)引用計數(shù)和線程安全
     Python對象引用計數(shù)的宏:Py_INCREF(obj)增加對象obj的引用計數(shù),Py_DECREF(obj)減少對象obj的引用計數(shù)。Py_INCREF()和Py_DECREF()兩個函數(shù)也有一個先檢查對象是否為空的版本,分別為Py_XINCREF()和Py_XDECREF()。
      編譯擴展的程序員必須要注意,代碼有可能會被運行在一個多線程的Python環(huán)境中。這些線程使用了兩個C宏P(guān)y_BEGIN_ALLOW_THREADS和Py_END_ALLOW_THREADS,通過將代碼和線程隔離,保證了運行和非運行時的安全性,由這些宏包裹的代碼將會允許其他線程的運行。

 

三、C/C 調(diào)用Python

       C 可以調(diào)用Python腳本,那么就可以寫一些Python的腳本接口供C 調(diào)用了,至少可以把Python當成文本形式的動態(tài)鏈接庫, 
需要的時候還可以改一改,只要不改變接口。缺點是C 的程序一旦編譯好了,再改就沒那么方便了。
(1)Python腳本:pytest.py

復制代碼
復制代碼
#test function def add(a,b): print 'in python function add' print 'a = ' str(a) print 'b = ' str(b) print 'ret = ' str(a b) return def foo(a): print 'in python function foo' print 'a = ' str(a) print 'ret = ' str(a * a) return class guestlist: def __init__(self): print 'aaaa' def p(): print 'bbbbb' def __getitem__(self, id): return 'ccccc' def update(): guest = guestlist() print guest['aa'] #update()
復制代碼
復制代碼

(2)C 代碼:

復制代碼
復制代碼
/**g -o callpy callpy.cpp -I/usr/include/python2.6 -L/usr/lib64/python2.6/config -lpython2.6**/ #include <Python.h> int main(int argc, char** argv) { // 初始化Python //在使用Python系統(tǒng)前,必須使用Py_Initialize對其 //進行初始化。它會載入Python的內(nèi)建模塊并添加系統(tǒng)路 //徑到模塊搜索路徑中。這個函數(shù)沒有返回值,檢查系統(tǒng) //是否初始化成功需要使用Py_IsInitialized。 Py_Initialize(); // 檢查初始化是否成功 if ( !Py_IsInitialized() ) { return -1; } // 添加當前路徑 //把輸入的字符串作為Python代碼直接運行,返回0 //表示成功,-1表示有錯。大多時候錯誤都是因為字符串 //中有語法錯誤。 PyRun_SimpleString('import sys'); PyRun_SimpleString('print '---import sys---''); PyRun_SimpleString('sys.path.append('./')'); PyObject *pName,*pModule,*pDict,*pFunc,*pArgs; // 載入名為pytest的腳本 pName = PyString_FromString('pytest'); pModule = PyImport_Import(pName); if ( !pModule ) { printf('can't find pytest.py'); getchar(); return -1; } pDict = PyModule_GetDict(pModule); if ( !pDict ) { return -1; } // 找出函數(shù)名為add的函數(shù) printf('----------------------\n'); pFunc = PyDict_GetItemString(pDict, 'add'); if ( !pFunc || !PyCallable_Check(pFunc) ) { printf('can't find function [add]'); getchar(); return -1; } // 參數(shù)進棧 *pArgs; pArgs = PyTuple_New(2); // PyObject* Py_BuildValue(char *format, ...) // 把C 的變量轉(zhuǎn)換成一個Python對象。當需要從 // C 傳遞變量到Python時,就會使用這個函數(shù)。此函數(shù) // 有點類似C的printf,但格式不同。常用的格式有 // s 表示字符串, // i 表示整型變量, // f 表示浮點數(shù), // O 表示一個Python對象。 PyTuple_SetItem(pArgs, 0, Py_BuildValue('l',3)); PyTuple_SetItem(pArgs, 1, Py_BuildValue('l',4)); // 調(diào)用Python函數(shù) PyObject_CallObject(pFunc, pArgs); //下面這段是查找函數(shù)foo 并執(zhí)行foo printf('----------------------\n'); pFunc = PyDict_GetItemString(pDict, 'foo'); if ( !pFunc || !PyCallable_Check(pFunc) ) { printf('can't find function [foo]'); getchar(); return -1; } pArgs = PyTuple_New(1); PyTuple_SetItem(pArgs, 0, Py_BuildValue('l',2)); PyObject_CallObject(pFunc, pArgs); printf('----------------------\n'); pFunc = PyDict_GetItemString(pDict, 'update'); if ( !pFunc || !PyCallable_Check(pFunc) ) { printf('can't find function [update]'); getchar(); return -1; } pArgs = PyTuple_New(0); PyTuple_SetItem(pArgs, 0, Py_BuildValue('')); PyObject_CallObject(pFunc, pArgs); Py_DECREF(pName); Py_DECREF(pArgs); Py_DECREF(pModule); // 關(guān)閉Python Py_Finalize(); return 0; }
復制代碼
復制代碼

(3)C 編譯成二進制可執(zhí)行文件:g -o callpy callpy.cpp -I/usr/include/python2.6 -L/usr/lib64/python2.6/config -lpython2.6,編譯選項需要手動指定Python的include路徑和鏈接接路徑(Python版本號根據(jù)具體情況而定)。

(4)運行結(jié)果:

 

四、總結(jié)

(1)Python和C/C 的相互調(diào)用僅是測試代碼,具體的項目開發(fā)還得參考Python的API文檔。
(2)兩者交互,C 可為Python編寫擴展模塊,Python也可為C 提供腳本接口,更加方便于實際應用。
(3)若有不足,請留言,在此先感謝!

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多