欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

在python中調(diào)用C/C++的三種方法

 更新時(shí)間:2024年02月02日 15:02:47   作者:哦豁灬  
這篇文章主要給大家介紹了關(guān)于在python中調(diào)用C/C++的三種方法,Python可以通過(guò)調(diào)用C/C++接口來(lái)實(shí)現(xiàn)與C/C++語(yǔ)言的交互,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下

Python 是一種很好用的膠水語(yǔ)言,利用Python的簡(jiǎn)潔和C++的高效,基本可以解決99%的問(wèn)題了,剩下那 1% 的問(wèn)題也就不是問(wèn)題了,畢竟不是所有問(wèn)題都可解。

一般的,Python和C++的交互分為這兩種情況:

  • 用C++擴(kuò)展Python:當(dāng)一個(gè)Python項(xiàng)目中出現(xiàn)了性能瓶頸時(shí),將瓶頸部分抽離出來(lái),用C++封裝成一個(gè)Python可以調(diào)用的模塊(so庫(kù));
  • 將Python內(nèi)嵌入C++:當(dāng)一個(gè)C++項(xiàng)目中有部分功能預(yù)期將會(huì)經(jīng)常變更需求,期望獲得更高的靈活性時(shí),將這部分功能用Python實(shí)現(xiàn),在C++中進(jìn)行調(diào)用。

這里討論前者,在 python 中調(diào)用 C/C++ 代碼的方法很多,這里記錄三種方法的使用。

1 C/C++ 編譯成可執(zhí)行文件,python 通過(guò) subprocess 調(diào)用

C/C++ 代碼正常編寫(xiě),然后編譯成 exe/elf 格式的可執(zhí)行文件,Python 利用 subprocess 調(diào)用該可執(zhí)行文件即可。好處是改動(dòng)小,不好是至少需要兩個(gè)進(jìn)程跑代碼,而且 C/C++ 和 Python 通訊比較麻煩。

這種方法簡(jiǎn)單粗暴,不太好用,沒(méi)什么好說(shuō)的。

2 ctypes

C/C++ 在編寫(xiě)代碼的時(shí)候略微改動(dòng),然后編譯成 dll/so 格式的動(dòng)態(tài)庫(kù)文件,Python 利用 ctypes 調(diào)用該庫(kù)文件即可。好處一個(gè)進(jìn)程內(nèi)運(yùn)行,C/C++ 側(cè)改動(dòng)小,壞處是 Python 側(cè)需適配代碼比較多。

ctypes 是 python 自帶的一個(gè)庫(kù),可以用來(lái)調(diào)用 c/cpp 的動(dòng)態(tài)鏈接庫(kù)。使用 ctypes 調(diào)用 c++ 代碼步驟如下:

  • 編寫(xiě) cpp 代碼,將其編譯成動(dòng)態(tài)鏈接庫(kù)(.so 或者 .dll 文件)。
  • 在 python 代碼文件中導(dǎo)入 ctypes 庫(kù),并使用 ctypes.cdll.LoadLibrary() 方法加載動(dòng)態(tài)鏈接庫(kù)。
  • 使用 ctypes 定義 c++ 函數(shù)的參數(shù)類型和返回值類型,并調(diào)用 c++ 函數(shù)。

2.1 編譯 C++

一個(gè)簡(jiǎn)單的 demo:

dll.cpp

extern "C" int add(int a, int b) {
	return a + b;
}

在目錄 python_call_c_cpp 下,使用 g++ 編譯 dll.cpp

g++ --shared -fPIC dll.cpp -o libadd.so

編譯完成后,在目錄下會(huì)生成一個(gè) libadd.so 文件:

2.2 python 調(diào)用 C/C++ 庫(kù)

main.py

import ctypes

# 加載動(dòng)態(tài)鏈接庫(kù) 
lib = ctypes.cdll.LoadLibrary("./libadd.so") 
# 定義函數(shù)參數(shù)類型和返回值類型 
lib.add.argtypes = [ctypes.c_int, ctypes.c_int] 
lib.add.restype = ctypes.c_int 

# 調(diào)用 C++ 函數(shù) 
result = lib.add(1, 2) 
print("調(diào)用C++庫(kù)的結(jié)果:" + str(result))

執(zhí)行 python3 main.py:

3 Boost.Python

Boost作為一個(gè)大寶庫(kù),提供了我們所需要的這一功能。并且,在Boost的許多庫(kù)中,已經(jīng)默認(rèn)使用了Boost.Python,所以也算是經(jīng)過(guò)了充分的測(cè)試。

3.1 安裝

Boost的大部分功能都是以頭文件的形式提供的,無(wú)需安裝;但是也有少部分功能,需要進(jìn)行手動(dòng)編譯。Boost.Python 需要進(jìn)行手動(dòng)編譯。

3.2 一個(gè)簡(jiǎn)單的 demo

用C++實(shí)現(xiàn)一個(gè)模塊,在Python中調(diào)用時(shí),可以返回一個(gè)特定的字符串。

#include <boost/python.hpp>

char const* greet()
{
	return "hello, boost";
}

BOOST_PYTHON_MODULE(hello_boostpy)
{
	using namespace boost::python;
	def("greet", greet);
}

將其編譯成動(dòng)態(tài)鏈接庫(kù)的形式:

g++ -I /usr/include/python2.7/ -fPIC -shared -o hello_boostpy.so http://hello_boostpy.cc -lboost_python

這時(shí)可以使用ldd看看hello_boostpy.so可不可以找到libboost_python,找不到的話,需要手動(dòng)將其路徑加入環(huán)境變量LD_LIBRARY_PATH中,或者用ldconfig相關(guān)的命令也可以。

在Python中使用hello_boostpy庫(kù):

# -*- coding: utf-8 -*-
import sys
sys.path.append('.')

def test():
    import hello_boostpy
    return hello_boostpy.greet()

if __name__ == "__main__":
    print test()

接下來(lái),我們?cè)贑++實(shí)現(xiàn)的模塊中,添加一個(gè)類,并且嘗試向C++方向傳入Python的list類型對(duì)象。

C++ 類:

#include <boost/python.hpp>
#include <vector>
#include <string>
#include <sstream>
using namespace boost::python;

struct Person
{
	void set_name(std::string name) { this->name = name; }
	std::string print_info();
	void set_items(list& prices, list& discounts);
	
	
	std::string name;
	std::vector<double> item_prices;
	std::vector<double> item_discounts;
};

其中,Python方的list類型,在Boost.Python中有一個(gè)對(duì)應(yīng)的實(shí)現(xiàn)boost::python::list(相應(yīng)的,dict、tuple等類型都有對(duì)應(yīng)實(shí)現(xiàn))。在set_items中,我們將會(huì)用boost::python::extract對(duì)數(shù)據(jù)類型做一個(gè)轉(zhuǎn)換。

void Person::set_items(list& prices, list& discounts)
{
	for(int i = 0; i < len(prices); ++i)
	{
		double price = extract<double>(prices[i]);
		double discount = extract<double>(discounts[i]);
		item_prices.push_back(price);
		item_discounts.push_back(discount);
	}
}

Python模塊定義部分依舊是非常直觀的代碼:

BOOST_PYTHON_MODULE(person)
{
	class_<Person>("Person")
		.def("set_name", &Person::set_name)
		.def("print_info", &Person::print_info)
		.def("set_items", &Person::set_items)
	;	
}

在Python代碼中,就可以像使用一個(gè)Python定義的類一樣使用Person類了:

# -*- coding: utf-8 -*-
import sys
sys.path.append('.')

def test():
    import person
    p = person.Person()
    p.set_name('Qie')
    p.set_items([100, 123.456, 888.8], [0.3, 0.1, 0.5])
    print p.print_info()

if __name__ == "__main__":
    test()

附c++調(diào)用Python:

將Python安裝目錄下的include和libs文件夾引入到項(xiàng)目中

將libs目錄下的python37.lib復(fù)制一份為python37_d.lib

1、Python腳本

def Hello():
    print("Hello")
     
def Add(a,b):
    return  a+b

2、C++調(diào)用python腳本

#include <Python.h>
using namespace std;
 
int main()
{
    Py_Initialize();              //初始化,創(chuàng)建一個(gè)Python虛擬環(huán)境
    if (Py_IsInitialized())
    {
        PyObject* pModule = NULL;
        PyObject* pFunc = NULL;
        pModule = PyImport_ImportModule("test_python");  //參數(shù)為Python腳本的文件名
        if (pModule)
        {
            pFunc = PyObject_GetAttrString(pModule, "Hello");   //獲取函數(shù)
            PyEval_CallObject(pFunc, NULL);           //執(zhí)行函數(shù)
        }
        else
        {
            printf("導(dǎo)入Python模塊失敗...\n");
        }
    }
    else
    {
        printf("Python環(huán)境初始化失敗...\n");
    }
    Py_Finalize();
}

接口方法

Python3.6提供給C/C++接口函數(shù),基本都是定義pylifecycle.h,pythonrun.h,ceval.h中。

  • Py_Initialize() 和 Py_Finalize()
    必須先調(diào)用Py_Initialize()進(jìn)行初始化,這個(gè)API用來(lái)分配Python解釋器使用的全局資源,應(yīng)用程序結(jié)束時(shí)需要調(diào)用Py_Finalize()來(lái)關(guān)閉Python的使用環(huán)境。
  • Py_IsInitialized()
    用來(lái)判斷Python解釋器是否初始化成功,true為成功,false為失敗。
  • PyErr_Print() & PyErr_Clear()
    執(zhí)行Python出錯(cuò)時(shí),PyErr_Print()可將錯(cuò)誤信息顯示出來(lái),PyErr_Clear()將錯(cuò)誤信息在Python解釋器的緩存清除。
  • PyRun_SimpleString()
    這個(gè)函數(shù)能夠用來(lái)執(zhí)行簡(jiǎn)單的Python語(yǔ)句。
  • PyEval_InitThreads()
    如果使用多線程調(diào)用Python腳本,就需要在初始化Python解釋器時(shí)調(diào)用PyEval_InitThreads()來(lái)啟用線程支持(導(dǎo)致Python內(nèi)部啟用線程鎖),最好在主線程啟動(dòng)時(shí)就調(diào)用。該API同時(shí)也鎖定全局解釋鎖,所以,還需要在初始化完成后需要自行釋放鎖。
    如果不需要使用多線程,不建議啟用該選項(xiàng),互斥鎖也會(huì)不可避免的增加系統(tǒng)開(kāi)銷。

3、規(guī)范化語(yǔ)法

#include<Python.h> //添加python的聲明
 
using namespace std;
 
int main()
{
Py_Initialize(); //1、初始化python接口
 
//初始化使用的變量
PyObject* pModule = NULL;
PyObject* pFunc = NULL;
PyObject* pName = NULL;
 
//2、初始化python系統(tǒng)文件路徑,保證可以訪問(wèn)到 .py文件
PyRun_SimpleString("import sys");
PyRun_SimpleString("sys.path.append('./')");
 
//3、調(diào)用python文件名。當(dāng)前的測(cè)試python文件名是test.py。在使用這個(gè)函數(shù)的時(shí)候,只需要寫(xiě)文件的名稱就可以了。不用寫(xiě)后綴。
pModule = PyImport_ImportModule("test");
 
//4、調(diào)用函數(shù)
pFunc = PyObject_GetAttrString(pModule, "AdditionFc");
 
//5、給python傳參數(shù)
PyObject* pArgs = PyTuple_New(2);//函數(shù)調(diào)用的參數(shù)傳遞均是以元組的形式打包的,2表示參數(shù)個(gè)數(shù)。如果AdditionFc中只有一個(gè)參數(shù)時(shí),寫(xiě)1就可以了。這里只先介紹函數(shù)必須有參數(shù)存在的情況。
 
 
PyTuple_SetItem(pArgs, 0, Py_BuildValue("i", 2)); //0:表示序號(hào)。第一個(gè)參數(shù)。
PyTuple_SetItem(pArgs, 1, Py_BuildValue("i", 4)); //1:也表示序號(hào)。第二個(gè)參數(shù)。i:表示傳入的參數(shù)類型是int類型。
 
//6、使用C++的python接口調(diào)用該函數(shù)
PyObject* pReturn = PyEval_CallObject(pFunc, pArgs);
 
//7、接收python計(jì)算好的返回值
int nResult;
PyArg_Parse(pReturn, "i", &nResult);//i表示轉(zhuǎn)換成int型變量。在這里,最需要注意的是:PyArg_Parse的最后一個(gè)參數(shù),必須加上“&”符號(hào)。
 
//8、結(jié)束python接口初始化
Py_Finalize();
}

總結(jié) 

到此這篇關(guān)于在python中調(diào)用C/C++的三種方法的文章就介紹到這了,更多相關(guān)python調(diào)用C/C++內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Python的Flask框架應(yīng)用調(diào)用Redis隊(duì)列數(shù)據(jù)的方法

    Python的Flask框架應(yīng)用調(diào)用Redis隊(duì)列數(shù)據(jù)的方法

    這里為大家?guī)?lái)Python的Flask框架應(yīng)用調(diào)用Redis隊(duì)列數(shù)據(jù)的方法,從而能夠?qū)崿F(xiàn)異步無(wú)阻塞從而提高某些實(shí)時(shí)處理情況下程序的性能,需要的朋友可以參考下
    2016-06-06
  • 基于python3實(shí)現(xiàn)socket文件傳輸和校驗(yàn)

    基于python3實(shí)現(xiàn)socket文件傳輸和校驗(yàn)

    這篇文章主要為大家詳細(xì)介紹了基于python3實(shí)現(xiàn)socket文件傳輸和校驗(yàn),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-07-07
  • django項(xiàng)目中使用手機(jī)號(hào)登錄的實(shí)例代碼

    django項(xiàng)目中使用手機(jī)號(hào)登錄的實(shí)例代碼

    這篇文章主要介紹了django項(xiàng)目中使用手機(jī)號(hào)登錄的實(shí)例代碼,非常不錯(cuò),具有一定的參考借鑒價(jià)值 ,需要的朋友可以參考下
    2019-08-08
  • Python Django ORM與模型詳解

    Python Django ORM與模型詳解

    這篇文章主要介紹了django的ORM與模型的實(shí)現(xiàn)原理,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧v
    2021-11-11
  • python密碼學(xué)黑客攻擊RSA密碼

    python密碼學(xué)黑客攻擊RSA密碼

    這篇文章主要為大家介紹了python密碼學(xué)黑客攻擊RSA密碼,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-05-05
  • 最新評(píng)論