电脑知识|欧美黑人一区二区三区|软件|欧美黑人一级爽快片淫片高清|系统|欧美黑人狂野猛交老妇|数据库|服务器|编程开发|网络运营|知识问答|技术教程文章 - 好吧啦网

您的位置:首頁技術文章
文章詳情頁

Python與C/C++的相互調用案例

瀏覽:53日期:2022-06-26 13:52:27
一、問題

Python模塊和C/C++的動態庫間相互調用在實際的應用中會有所涉及,在此作一總結。

二、Python調用C/C++1、Python調用C動態鏈接庫

Python調用C庫比較簡單,不經過任何封裝打包成so,再使用python的ctypes調用即可。

(1)C語言文件:pycall.c

/***gcc -o libpycall.so -shared -fPIC pycall.c*/#include <stdio.h>#include <stdlib.h>int foo(int a, int b){ printf('you input %d and %dn', a, b); return a+b;}

(2)gcc編譯生成動態庫libpycall.so:gcc -o libpycall.so -shared -fPIC pycall.c。使用g++編譯生成C動態庫的代碼中的函數或者方法時,需要使用extern 'C'來進行編譯。

(3)Python調用動態庫的文件:pycall.py

import ctypesll = ctypes.cdll.LoadLibrary lib = ll('./libpycall.so') lib.foo(1, 3)print ’***finish***’

(4)運行結果:

Python與C/C++的相互調用案例

2、Python調用C++(類)動態鏈接庫

需要extern 'C'來輔助,也就是說還是只能調用C函數,不能直接調用方法,但是能解析C++方法。不是用extern 'C',構建后的動態鏈接庫沒有這些函數的符號表。

(1)C++類文件:pycallclass.cpp

#include <iostream>using namespace std; class TestLib{ public: void display(); void display(int a);};void TestLib::display() { cout<<'First display'<<endl;}void TestLib::display(int a) { cout<<'Second display:'<<a<<endl;}extern 'C' { TestLib obj; void display() { obj.display(); } void display_int() { obj.display(2); }}

(2)g++編譯生成動態庫libpycall.so:g++ -o libpycallclass.so -shared -fPIC pycallclass.cpp。

(3)Python調用動態庫的文件:pycallclass.py

import ctypesso = ctypes.cdll.LoadLibrary lib = so('./libpycallclass.so') print ’display()’lib.display()print ’display(100)’lib.display_int(100)

(4)運行結果:

Python與C/C++的相互調用案例

3、Python調用C/C++可執行程序

(1)C/C++程序:main.cpp

#include <iostream>using namespace std;int test(){ int a = 10, b = 5; return a+b;}int main(){ cout<<'---begin---'<<endl; int num = test(); cout<<'num='<<num<<endl; cout<<'---end---'<<endl;}

(2)編譯成二進制可執行文件:g++ -o testmain main.cpp。

(3) Python調用程序:main.py

import commandsimport osmain = './testmain'if os.path.exists(main): rc, out = commands.getstatusoutput(main) print ’rc = %d, nout = %s’ % (rc, out) print ’*’*10f = os.popen(main) data = f.readlines() f.close() print data print ’*’*10os.system(main)

(4)運行結果:

Python與C/C++的相互調用案例

4、擴展Python(C++為Python編寫擴展模塊)

所有能被整合或導入到其它python腳本的代碼,都可以被稱為擴展。可以用Python來寫擴展,也可以用C和C++之類的編譯型的語言來寫擴展。Python在設計之初就考慮到要讓模塊的導入機制足夠抽象。抽象到讓使用模塊的代碼無法了解到模塊的具體實現細節。Python的可擴展性具有的優點:方便為語言增加新功能、具有可定制性、代碼可以實現復用等。

為 Python 創建擴展需要三個主要的步驟:創建應用程序代碼、利用樣板來包裝代碼和編譯與測試。

(1)創建應用程序代碼

#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! == %dn', fac(4)); printf('8! == %dn', fac(8)); printf('12! == %dn', 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;}

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

(2)用樣板來包裝代碼

接口的代碼被稱為“樣板”代碼,它是 應用程序代碼與Python解釋器之間進行交互所必不可少的一部分。樣板主要分為4步:a、包含Python的頭文件;b、為每個模塊的每一個函數增加一個型如PyObject* Module_func()的包裝函數;c、為每個模塊增加一個型如PyMethodDef ModuleMethods[]的數組;d、增加模塊初始化函數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! == %dn', fac(4)); printf('8! == %dn', fac(8)); printf('12! == %dn', 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); #防止內存泄漏 return retval;}static PyObject *Extest_test(PyObject *self, PyObject *args){ test(); return (PyObject*)Py_BuildValue('');}static PyMethodDefExtestMethods[] ={ { '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頭文件在大多數類Unix系統中會在/usr/local/include/python2.x或/usr/include/python2.x目錄中,系統一般都會知道文件安裝的路徑。

增加包裝函數,所在模塊名為Extest,那么創建一個包裝函數叫Extest_fac(),在Python腳本中使用是先import Extest,然后調用Extest.fac(),當 Extest.fac()被調用時,包裝函數 Extest_fac()會被調用,包裝函數接受一個 Python的整數參數,把它轉為C的整數,然后調用C的fac()函數,得到一個整型的返回值,最后把這個返回值轉為Python的整型數做為整個函數調用的結果返回回去。其他兩個包裝函數Extest_doppel()和Extest_test()類似。

從Python到C的轉換用PyArg_Parse*系列函數, int PyArg_ParseTuple():把Python傳過來的參數轉為C;int PyArg_ParseTupleAndKeywords()與PyArg_ParseTuple()作用相同,但是同時解析關鍵字參數;它們 的用法跟C的sscanf函數很像,都接受一個字符串流,并根據一個指定的格式字符串進行解析,把結果放入到相應的指針所指的變量中去,它們的返回值為1表示解析成功,返回值為0表示失敗。 從C到Python的轉換函數是PyObject* Py_BuildValue():把C的數據轉為Python的一個對象或一組對象,然后返回之;Py_BuildValue的用法跟sprintf很像,把所有的參數按格式字符串所指定的格式轉換成一個Python的對象。

C與Python之間數據轉換的轉換代碼:

Python與C/C++的相互調用案例

為每個模塊增加一個型如PyMethodDef ModuleMethods[]的數組,以便于Python解釋器能夠導入并調用它們,每一個數組都包含了函數在Python中的名字,相應的包裝函數的名字以及一個METH_VARARGS常量,METH_VARARGS表示參數以tuple形式傳入。 若需要使用 PyArg_ParseTupleAndKeywords()函數來分析命名參數的話,還需要讓這個標志常量與METH_KEYWORDS常量進行邏輯與運算常量 。數組最后用兩個NULL來表示函數信息列表的結束。

所有工作的最后一部分就是模塊的初始化函數,調用Py_InitModule()函數,并把模塊名和ModuleMethods[]數組的名字傳遞進去,以便于解釋器能正確的調用模塊中的函數。

(3)編譯

為了讓新Python的擴展能被創建,需要把它們與Python庫放在一起編譯,distutils包被用來編譯、安裝和分發這些模塊、擴展和包。

創建一個setup.py 文件,編譯最主要的工作由setup()函數來完成:

#!/usr/bin/env python from distutils.core import setup, Extension MOD = ’Extest’setup(name=MOD, ext_modules=[Extension(MOD, sources=[’Extest2.c’])])

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

運行setup.py build命令就可以開始編譯我們的擴展了,提示部分信息:

creating build/lib.linux-x86_64-2.6gcc -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)導入和測試

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

測試模塊:

Python與C/C++的相互調用案例

(5)引用計數和線程安全

Python對象引用計數的宏:Py_INCREF(obj)增加對象obj的引用計數,Py_DECREF(obj)減少對象obj的引用計數。Py_INCREF()和Py_DECREF()兩個函數也有一個先檢查對象是否為空的版本,分別為Py_XINCREF()和Py_XDECREF()。

編譯擴展的程序員必須要注意,代碼有可能會被運行在一個多線程的Python環境中。這些線程使用了兩個C宏Py_BEGIN_ALLOW_THREADS和Py_END_ALLOW_THREADS, 通過將代碼和線程隔離,保證了運行和非運行時的安全性,由這些宏包裹的代碼將會允許其他線程的運行。

三、C/C++調用Python

C++可以調用Python腳本,那么就可以寫一些Python的腳本接口供C++調用了,至少可以把Python當成文本形式的動態鏈接庫,

需要的時候還可以改一改,只要不改變接口。缺點是C++的程序一旦編譯好了,再改就沒那么方便了。

(1)Python腳本:pytest.py

#test functiondef 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系統前,必須使用Py_Initialize對其 //進行初始化。它會載入Python的內建模塊并添加系統路 //徑到模塊搜索路徑中。這個函數沒有返回值,檢查系統 //是否初始化成功需要使用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; } // 找出函數名為add的函數 printf('----------------------n'); pFunc = PyDict_GetItemString(pDict, 'add'); if ( !pFunc || !PyCallable_Check(pFunc) ) { printf('can’t find function [add]'); getchar(); return -1; } // 參數進棧 PyObject *pArgs; pArgs = PyTuple_New(2); // PyObject* Py_BuildValue(char *format, ...) // 把C++的變量轉換成一個Python對象。當需要從 // C++傳遞變量到Python時,就會使用這個函數。此函數 // 有點類似C的printf,但格式不同。常用的格式有 // s 表示字符串, // i 表示整型變量, // f 表示浮點數, // O 表示一個Python對象。 PyTuple_SetItem(pArgs, 0, Py_BuildValue('l',3)); PyTuple_SetItem(pArgs, 1, Py_BuildValue('l',4)); // 調用Python函數 PyObject_CallObject(pFunc, pArgs); //下面這段是查找函數foo 并執行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); // 關閉Python Py_Finalize(); return 0;}

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

(4)運行結果:

Python與C/C++的相互調用案例

四、總結

(1)Python和C/C++的相互調用僅是測試代碼,具體的項目開發還得參考Python的API文檔。

(2)兩者交互,C++可為Python編寫擴展模塊,Python也可為C++提供腳本接口,更加方便于實際應用。

以上為個人經驗,希望能給大家一個參考,也希望大家多多支持好吧啦網。如有錯誤或未考慮完全的地方,望不吝賜教。

標簽: Python 編程
相關文章:
主站蜘蛛池模板: 锂电池砂磨机|石墨烯砂磨机|碳纳米管砂磨机-常州市奥能达机械设备有限公司 | 标准件-非标紧固件-不锈钢螺栓-非标不锈钢螺丝-非标螺母厂家-三角牙锁紧自攻-南京宝宇标准件有限公司 | 悬浮拼装地板_幼儿园_篮球场_悬浮拼接地板-山东悬浮拼装地板厂家 | 台湾阳明固态继电器-奥托尼克斯光电传感器-接近开关-温控器-光纤传感器-编码器一级代理商江苏用之宜电气 | 氧化锆陶瓷_氧化锆陶瓷加工_氧化锆陶瓷生产厂家-康柏工业陶瓷有限公司 | 根系分析仪,大米外观品质检测仪,考种仪,藻类鉴定计数仪,叶面积仪,菌落计数仪,抑菌圈测量仪,抗生素效价测定仪,植物表型仪,冠层分析仪-杭州万深检测仪器网 | 骨密度检测仪_骨密度分析仪_骨密度仪_动脉硬化检测仪专业生产厂家【品源医疗】 | 防火门-专业生产甲级不锈钢钢质防火门厂家资质齐全-广东恒磊安防设备有限公司 | 全温度恒温培养摇床-大容量-立式-远红外二氧化碳培养箱|南荣百科 | 定量包装秤,吨袋包装称,伸缩溜管,全自动包装秤,码垛机器人,无锡市邦尧机械工程有限公司 | 沧州友城管业有限公司-内外涂塑钢管-大口径螺旋钢管-涂塑螺旋管-保温钢管生产厂家 | 应急灯_消防应急灯_应急照明灯_应急灯厂家-大成智慧官网 | 免费个人pos机申请办理-移动pos机刷卡-聚合收款码办理 | 精密模具-双色注塑模具加工-深圳铭洋宇通| 铜镍-康铜-锰铜-电阻合金-NC003 - 杭州兴宇合金有限公司 | 河南橡胶接头厂家,河南波纹补偿器厂家,河南可曲挠橡胶软连接,河南套筒补偿器厂家-河南正大阀门 | 建筑消防设施检测系统检测箱-电梯**检测仪器箱-北京宇成伟业科技有限责任公司 | 全自动包装秤_全自动上袋机_全自动套袋机_高位码垛机_全自动包装码垛系统生产线-三维汉界机器(山东)股份有限公司 | 电气控制系统集成商-PLC控制柜变频控制柜-非标自动化定制-电气控制柜成套-NIDEC CT变频器-威肯自动化控制 | 电脑知识|软件|系统|数据库|服务器|编程开发|网络运营|知识问答|技术教程文章 - 好吧啦网 | 潜水搅拌机-双曲面搅拌机-潜水推进器|奥伯尔环保 | 细沙回收机-尾矿干排脱水筛设备-泥石分离机-建筑垃圾分拣机厂家-青州冠诚重工机械有限公司 | 臻知网大型互动问答社区-你的问题将在这里得到解答!-无锡据风网络科技有限公司 | b2b网站大全,b2b网站排名,找b2b网站就上地球网 | hdpe土工膜-防渗膜-复合土工膜-长丝土工布价格-厂家直销「恒阳新材料」-山东恒阳新材料有限公司 ETFE膜结构_PTFE膜结构_空间钢结构_膜结构_张拉膜_浙江萬豪空间结构集团有限公司 | 挤出熔体泵_高温熔体泵_熔体出料泵_郑州海科熔体泵有限公司 | FFU_空气初效|中效|高效过滤器_空调过滤网-广州梓净净化设备有限公司 | 双段式高压鼓风机-雕刻机用真空泵-绍兴天晨机械有限公司 | 全自动固相萃取仪_高通量真空平行浓缩仪-勤业永为 | 泰国试管婴儿_泰国第三代试管婴儿费用|成功率|医院—新生代海外医疗 | 华东师范大学在职研究生招生网_在职研究生招生联展网 | 高楼航空障碍灯厂家哪家好_航空障碍灯厂家_广州北斗星障碍灯有限公司 | 蓄电池在线监测系统|SF6在线监控泄露报警系统-武汉中电通电力设备有限公司 | 网站制作优化_网站SEO推广解决方案-无锡首宸信息科技公司 | 储气罐,真空罐,缓冲罐,隔膜气压罐厂家批发价格,空压机储气罐规格型号-上海申容压力容器集团有限公司 | 无锡网站建设-做网站-建网站-网页设计制作-阿凡达建站公司 | 同步带轮_同步带_同步轮_iHF合发齿轮厂家-深圳市合发齿轮机械有限公司 | 网站建设-高端品牌网站设计制作一站式定制_杭州APP/微信小程序开发运营-鼎易科技 | 福州仿石漆加盟_福建仿石漆厂家-外墙仿石漆加盟推荐铁壁金钢(福建)新材料科技有限公司有保障 | 常州减速机_减速机厂家_常州市减速机厂有限公司 | 恒压供水控制柜|无负压|一体化泵站控制柜|PLC远程调试|MCGS触摸屏|自动控制方案-联致自控设备 |