亚洲免费在线-亚洲免费在线播放-亚洲免费在线观看-亚洲免费在线观看视频-亚洲免费在线看-亚洲免费在线视频

Python與C/C++相互調用

系統 1787 0

?

原文鏈接

作者

一、問題

? ????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 
              
                  
#include 
                
                    
int foo(int a, int b)  
{  
  printf("you input %d and %d\n", 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 ctypes  
ll = ctypes.cdll.LoadLibrary   
lib = ll("./libpycall.so")    
lib.foo(1, 3)  
print '***finish***'  
              
復制代碼

(4)運行結果:


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

?????? 需要extern "C"來輔助,也就是說還是只能調用C函數,不能直接調用方法,但是能解析C++方法。不是用extern "C",構建后的動態鏈接庫沒有這些函數的符號表。
(1)C++類文件:pycallclass.cpp
復制代碼
復制代碼
                    #include 
                    
                        
using namespace std;  
  
class TestLib  
{  
    public:  
        void display();  
        void display(int a);  
};  
void TestLib::display() {  
    cout<<"First display"<
                      
                        
復制代碼
復制代碼

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

(3)Python調用動態庫的文件:pycallclass.py
復制代碼
復制代碼
                    import ctypes  
so = ctypes.cdll.LoadLibrary   
lib = so("./libpycallclass.so")   
print 'display()'  
lib.display()  
print 'display(100)'  
lib.display_int(100) 
                  
復制代碼
復制代碼

(4)運行結果:


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

(1)C/C++程序:main.cpp
復制代碼
復制代碼
                    #include 
                    
                        
using namespace std;  
int test()  
{  
    int a = 10, b = 5;  
    return a+b;  
}  
int main()  
{  
    cout<<"---begin---"<
                      
                    
                  
復制代碼
復制代碼

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

(3)Python調用程序:main.py
復制代碼
復制代碼
              import commands  
import os  
main = "./testmain"  
if os.path.exists(main):  
    rc, out = commands.getstatusoutput(main)  
    print 'rc = %d, \nout = %s' % (rc, out)  
  
print '*'*10  
f = os.popen(main)    
data = f.readlines()    
f.close()    
print data  
  
print '*'*10  
os.system(main)  
            
復制代碼
復制代碼

(4)運行結果:

?

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

?????? 所有能被整合或導入到其它python腳本的代碼,都可以被稱為擴展。可以用Python來寫擴展,也可以用C和C++之類的編譯型的語言來寫擴展。Python在設計之初就考慮到要讓模塊的導入機制足夠抽象。抽象到讓使用模塊的代碼無法了解到模塊的具體實現細節。Python的可擴展性具有的優點:方便為語言增加新功能、具有可定制性、代碼可以實現復用等。
?????? 為 Python 創建擴展需要三個主要的步驟:創建應用程序代碼、利用樣板來包裝代碼和編譯與測試。
(1)創建應用程序代碼
復制代碼
復制代碼
              #include 
              
                  
#include 
                
                    
#include 
                  
                      
  
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;  
}  
                  
                
              
            
復制代碼
復制代碼

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

(2)用樣板來包裝代碼
??????? 接口的代碼被稱為“樣板”代碼,它是應用程序代碼與Python解釋器之間進行交互所必不可少的一部分。樣板主要分為4步:a、包含Python的頭文件;b、為每個模塊的每一個函數增加一個型如PyObject* Module_func()的包裝函數;c、為每個模塊增加一個型如PyMethodDef ModuleMethods[]的數組;d、增加模塊初始化函數void initModule()。
復制代碼
復制代碼
                    #include 
                    
                        
#include 
                      
                          
#include 
                        
                            
  
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);             #防止內存泄漏  
    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頭文件在大多數類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之間數據轉換的轉換代碼:

??????? 為每個模塊增加一個型如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.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)導入和測試

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


(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 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 
              
                  
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;  
     }  
  
    // 參數進棧  
    *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)運行結果:

?


更多文章、技術交流、商務合作、聯系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號聯系: 360901061

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對您有幫助就好】

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長會非常 感謝您的哦!!!

發表我的評論
最新評論 總共0條評論
主站蜘蛛池模板: 色91在线 | 亚洲一区二区三区在线免费观看 | 毛片a级放荡的护士hd | 国产精品一 | 中文日产国产精品久久 | 国产精品伦视频观看免费 | 成人一区专区在线观看 | 久久精品人人做人人看最新章 | 亚洲成综合 | 久久久久久久久久免观看 | 老司机免费福利视频无毒午夜 | 国产午夜精品一二区理论影院 | 亚洲高清中文字幕一区二区三区 | 精品国产品香蕉在线 | 国产一级在线免费观看 | 99超级碰碰成人香蕉网 | 亚洲精品视频在线播放 | 成人性视频免费网站 | 亚洲一区二区三区一品精 | 精品在线视频观看 | 亚洲欧美综合人成野草 | 国产精欧美一区二区三区 | 中文字幕第5页 | 国产成人久视频免费 | 国产精品第1页在线播放 | 天天成人综合网 | 青青草国产三级精品三级 | 久久久视频在线 | 久久99国产这里有精品视 | 久久久久久久国产精品视频 | 久久综合九色综合欧美狠狠 | 精品久久网站 | 四虎最新紧急入口4hu | 久久久夜色精品国产噜噜 | 奇米在线影视 | 欧美日韩亚洲国产精品一区二区 | 久久亚洲综合中文字幕 | 加勒比色综合 | 欧做爰xxxⅹ性欧美大片孕妇 | 亚洲精品乱码久久久久 | 色99色|