在python中調用C/C++的三種方法
Python 是一種很好用的膠水語言,利用Python的簡潔和C++的高效,基本可以解決99%的問題了,剩下那 1% 的問題也就不是問題了,畢竟不是所有問題都可解。
一般的,Python和C++的交互分為這兩種情況:
- 用C++擴展Python:當一個Python項目中出現(xiàn)了性能瓶頸時,將瓶頸部分抽離出來,用C++封裝成一個Python可以調用的模塊(so庫);
- 將Python內嵌入C++:當一個C++項目中有部分功能預期將會經(jīng)常變更需求,期望獲得更高的靈活性時,將這部分功能用Python實現(xiàn),在C++中進行調用。
這里討論前者,在 python 中調用 C/C++ 代碼的方法很多,這里記錄三種方法的使用。
1 C/C++ 編譯成可執(zhí)行文件,python 通過 subprocess 調用
C/C++ 代碼正常編寫,然后編譯成 exe/elf 格式的可執(zhí)行文件,Python 利用 subprocess 調用該可執(zhí)行文件即可。好處是改動小,不好是至少需要兩個進程跑代碼,而且 C/C++ 和 Python 通訊比較麻煩。
這種方法簡單粗暴,不太好用,沒什么好說的。
2 ctypes
C/C++ 在編寫代碼的時候略微改動,然后編譯成 dll/so 格式的動態(tài)庫文件,Python 利用 ctypes 調用該庫文件即可。好處一個進程內運行,C/C++ 側改動小,壞處是 Python 側需適配代碼比較多。
ctypes 是 python 自帶的一個庫,可以用來調用 c/cpp 的動態(tài)鏈接庫。使用 ctypes 調用 c++ 代碼步驟如下:
- 編寫 cpp 代碼,將其編譯成動態(tài)鏈接庫(.so 或者 .dll 文件)。
- 在 python 代碼文件中導入 ctypes 庫,并使用 ctypes.cdll.LoadLibrary() 方法加載動態(tài)鏈接庫。
- 使用 ctypes 定義 c++ 函數(shù)的參數(shù)類型和返回值類型,并調用 c++ 函數(shù)。
2.1 編譯 C++
一個簡單的 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
編譯完成后,在目錄下會生成一個 libadd.so 文件:
2.2 python 調用 C/C++ 庫
main.py
import ctypes # 加載動態(tài)鏈接庫 lib = ctypes.cdll.LoadLibrary("./libadd.so") # 定義函數(shù)參數(shù)類型和返回值類型 lib.add.argtypes = [ctypes.c_int, ctypes.c_int] lib.add.restype = ctypes.c_int # 調用 C++ 函數(shù) result = lib.add(1, 2) print("調用C++庫的結果:" + str(result))
執(zhí)行 python3 main.py
:
3 Boost.Python
Boost作為一個大寶庫,提供了我們所需要的這一功能。并且,在Boost的許多庫中,已經(jīng)默認使用了Boost.Python,所以也算是經(jīng)過了充分的測試。
3.1 安裝
Boost的大部分功能都是以頭文件的形式提供的,無需安裝;但是也有少部分功能,需要進行手動編譯。Boost.Python 需要進行手動編譯。
3.2 一個簡單的 demo
用C++實現(xiàn)一個模塊,在Python中調用時,可以返回一個特定的字符串。
#include <boost/python.hpp> char const* greet() { return "hello, boost"; } BOOST_PYTHON_MODULE(hello_boostpy) { using namespace boost::python; def("greet", greet); }
將其編譯成動態(tài)鏈接庫的形式:
g++ -I /usr/include/python2.7/ -fPIC -shared -o hello_boostpy.so http://hello_boostpy.cc -lboost_python
這時可以使用ldd看看hello_boostpy.so可不可以找到libboost_python,找不到的話,需要手動將其路徑加入環(huán)境變量LD_LIBRARY_PATH中,或者用ldconfig相關的命令也可以。
在Python中使用hello_boostpy庫:
# -*- coding: utf-8 -*- import sys sys.path.append('.') def test(): import hello_boostpy return hello_boostpy.greet() if __name__ == "__main__": print test()
接下來,我們在C++實現(xiàn)的模塊中,添加一個類,并且嘗試向C++方向傳入Python的list類型對象。
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中有一個對應的實現(xiàn)boost::python::list(相應的,dict、tuple等類型都有對應實現(xiàn))。在set_items中,我們將會用boost::python::extract對數(shù)據(jù)類型做一個轉換。
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代碼中,就可以像使用一個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++調用Python:
將Python安裝目錄下的include和libs文件夾引入到項目中
將libs目錄下的python37.lib復制一份為python37_d.lib
1、Python腳本
def Hello(): print("Hello") def Add(a,b): return a+b
2、C++調用python腳本
#include <Python.h> using namespace std; int main() { Py_Initialize(); //初始化,創(chuàng)建一個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("導入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()
必須先調用Py_Initialize()進行初始化,這個API用來分配Python解釋器使用的全局資源,應用程序結束時需要調用Py_Finalize()來關閉Python的使用環(huán)境。 - Py_IsInitialized()
用來判斷Python解釋器是否初始化成功,true為成功,false為失敗。 - PyErr_Print() & PyErr_Clear()
執(zhí)行Python出錯時,PyErr_Print()可將錯誤信息顯示出來,PyErr_Clear()將錯誤信息在Python解釋器的緩存清除。 - PyRun_SimpleString()
這個函數(shù)能夠用來執(zhí)行簡單的Python語句。 - PyEval_InitThreads()
如果使用多線程調用Python腳本,就需要在初始化Python解釋器時調用PyEval_InitThreads()來啟用線程支持(導致Python內部啟用線程鎖),最好在主線程啟動時就調用。該API同時也鎖定全局解釋鎖,所以,還需要在初始化完成后需要自行釋放鎖。
如果不需要使用多線程,不建議啟用該選項,互斥鎖也會不可避免的增加系統(tǒng)開銷。
3、規(guī)范化語法
#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)文件路徑,保證可以訪問到 .py文件 PyRun_SimpleString("import sys"); PyRun_SimpleString("sys.path.append('./')"); //3、調用python文件名。當前的測試python文件名是test.py。在使用這個函數(shù)的時候,只需要寫文件的名稱就可以了。不用寫后綴。 pModule = PyImport_ImportModule("test"); //4、調用函數(shù) pFunc = PyObject_GetAttrString(pModule, "AdditionFc"); //5、給python傳參數(shù) PyObject* pArgs = PyTuple_New(2);//函數(shù)調用的參數(shù)傳遞均是以元組的形式打包的,2表示參數(shù)個數(shù)。如果AdditionFc中只有一個參數(shù)時,寫1就可以了。這里只先介紹函數(shù)必須有參數(shù)存在的情況。 PyTuple_SetItem(pArgs, 0, Py_BuildValue("i", 2)); //0:表示序號。第一個參數(shù)。 PyTuple_SetItem(pArgs, 1, Py_BuildValue("i", 4)); //1:也表示序號。第二個參數(shù)。i:表示傳入的參數(shù)類型是int類型。 //6、使用C++的python接口調用該函數(shù) PyObject* pReturn = PyEval_CallObject(pFunc, pArgs); //7、接收python計算好的返回值 int nResult; PyArg_Parse(pReturn, "i", &nResult);//i表示轉換成int型變量。在這里,最需要注意的是:PyArg_Parse的最后一個參數(shù),必須加上“&”符號。 //8、結束python接口初始化 Py_Finalize(); }
總結
到此這篇關于在python中調用C/C++的三種方法的文章就介紹到這了,更多相關python調用C/C++內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Python requests庫參數(shù)提交的注意事項總結
這篇文章主要給大家介紹了關于Python requests庫參數(shù)提交的注意事項,文中通過示例代碼和圖片介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2021-03-03Python matplotlib生成圖片背景透明的示例代碼
這篇文章主要介紹了Python matplotlib生成圖片背景透明的示例代碼,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2019-08-08利用python+ffmpeg合并B站視頻及格式轉換的實例代碼
這篇文章主要介紹了利用python+ffmpeg合并B站視頻及格式轉換的實例代碼,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-11-11Python的Flask框架應用調用Redis隊列數(shù)據(jù)的方法
這里為大家?guī)鞵ython的Flask框架應用調用Redis隊列數(shù)據(jù)的方法,從而能夠實現(xiàn)異步無阻塞從而提高某些實時處理情況下程序的性能,需要的朋友可以參考下2016-06-06基于python3實現(xiàn)socket文件傳輸和校驗
這篇文章主要為大家詳細介紹了基于python3實現(xiàn)socket文件傳輸和校驗,具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-07-07