您好,登錄后才能下訂單哦!
本文小編為大家詳細介紹“如何在C++中調用Python”,內容詳細,步驟清晰,細節處理妥當,希望這篇“如何在C++中調用Python”文章能幫助大家解決疑惑,下面跟著小編的思路慢慢深入,一起來學習新知識吧。
為了使用Python.h這個擴展項,我們需要安裝一個python*-dev而不是python*,這兩者略有區別,下面的案例展示的是在Ubuntu20.04下安裝python3.9-dev的方法:
dechin@ubuntu2004:~/projects/gitlab/dechin/$ sudo apt install python3.9-dev 正在讀取軟件包列表... 完成 正在分析軟件包的依賴關系樹 正在讀取狀態信息... 完成 下列軟件包是自動安裝的并且現在不需要了: chromium-codecs-ffmpeg-extra gstreamer1.0-vaapi libgstreamer-plugins-bad1.0-0 linux-headers-5.8.0-43-generic linux-hwe-5.8-headers-5.8.0-43 linux-image-5.8.0-43-generic linux-modules-5.8.0-43-generic linux-modules-extra-5.8.0-43-generic 使用'sudo apt autoremove'來卸載它(它們)。 將會同時安裝下列軟件: libexpat1-dev libpython3.9 libpython3.9-dev zlib1g-dev 下列【新】軟件包將被安裝: libexpat1-dev libpython3.9 libpython3.9-dev python3.9-dev zlib1g-dev 升級了 0 個軟件包,新安裝了 5 個軟件包,要卸載 0 個軟件包,有 30 個軟件包未被升級。 需要下載 6,613 kB 的歸檔。 解壓縮后會消耗 28.7 MB 的額外空間。 您希望繼續執行嗎? [Y/n] Y 獲取:1 http://repo.huaweicloud.com/ubuntu focal/main amd64 libexpat1-dev amd64 2.2.9-1build1 [116 kB] 獲取:2 http://repo.huaweicloud.com/ubuntu focal-updates/universe amd64 libpython3.9 amd64 3.9.0-5~20.04 [1,710 kB] 獲取:3 http://repo.huaweicloud.com/ubuntu focal-updates/universe amd64 libpython3.9-dev amd64 3.9.0-5~20.04 [4,119 kB] 獲取:4 http://repo.huaweicloud.com/ubuntu focal-updates/main amd64 zlib1g-dev amd64 1:1.2.11.dfsg-2ubuntu1.2 [155 kB] 獲取:5 http://repo.huaweicloud.com/ubuntu focal-updates/universe amd64 python3.9-dev amd64 3.9.0-5~20.04 [512 kB] 已下載 6,613 kB,耗時 4秒 (1,594 kB/s) 正在選中未選擇的軟件包 libexpat1-dev:amd64。 (正在讀取數據庫 ... 系統當前共安裝有 269544 個文件和目錄。) 準備解壓 .../libexpat1-dev_2.2.9-1build1_amd64.deb ... 正在解壓 libexpat1-dev:amd64 (2.2.9-1build1) ... 正在選中未選擇的軟件包 libpython3.9:amd64。 準備解壓 .../libpython3.9_3.9.0-5~20.04_amd64.deb ... 正在解壓 libpython3.9:amd64 (3.9.0-5~20.04) ... 正在選中未選擇的軟件包 libpython3.9-dev:amd64。 準備解壓 .../libpython3.9-dev_3.9.0-5~20.04_amd64.deb ... 正在解壓 libpython3.9-dev:amd64 (3.9.0-5~20.04) ... 正在選中未選擇的軟件包 zlib1g-dev:amd64。 準備解壓 .../zlib1g-dev_1%3a1.2.11.dfsg-2ubuntu1.2_amd64.deb ... 正在解壓 zlib1g-dev:amd64 (1:1.2.11.dfsg-2ubuntu1.2) ... 正在選中未選擇的軟件包 python3.9-dev。 準備解壓 .../python3.9-dev_3.9.0-5~20.04_amd64.deb ... 正在解壓 python3.9-dev (3.9.0-5~20.04) ... 正在設置 libpython3.9:amd64 (3.9.0-5~20.04) ... 正在設置 libexpat1-dev:amd64 (2.2.9-1build1) ... 正在設置 zlib1g-dev:amd64 (1:1.2.11.dfsg-2ubuntu1.2) ... 正在設置 libpython3.9-dev:amd64 (3.9.0-5~20.04) ... 正在設置 python3.9-dev (3.9.0-5~20.04) ... 正在處理用于 man-db (2.9.1-1) 的觸發器 ... 正在處理用于 libc-bin (2.31-0ubuntu9.2) 的觸發器 ...
安裝完成后,如果在當前命令行下運行python3.9,是可以看到一個python專屬的命令行界面的,可以通過exit()退出。但是我們這里側重的是跟C++的配合工作,因此我們更加關注lib和include目錄下是否有生成相關的目錄,可以執行如下指令進行查看:
dechin@ubuntu2004:~/projects/gitlab/dechin/$ ll /usr/lib/ | grep python drwxr-xr-x 26 root root 20480 5月 7 16:27 python2.7/ drwxr-xr-x 3 root root 4096 2月 10 02:47 python3/ drwxr-xr-x 30 root root 20480 5月 7 16:30 python3.8/ drwxr-xr-x 31 root root 12288 5月 20 16:31 python3.9/
這里我們看到有一個3.9的版本,也就是我們剛才安裝的版本,再看看include下的目錄:
dechin@ubuntu2004:~/projects/gitlab/dechin/$ ll /usr/include/ | grep python drwxr-xr-x 2 root root 4096 5月 7 16:31 python3.8/ drwxr-xr-x 4 root root 4096 5月 20 16:31 python3.9/
這里我們就可以看到一些區別了,有一些版本的python不一定會有這兩個目錄,但是只有具備了這兩個目錄,才能夠被C++調用。
這里我們使用的IDE是VS Code,但是上述提到的幾個路徑,在VS Code中默認是不被包含的,因此在代碼編輯的過程中在include <Python.h>這一步就會報錯了。這一章節的目的主要是解決IDE中的報錯問題,還不是最終運行中出現的問題,因為運行時我是通過命令行執行g++來運行的,而不是直接用IDE來跑。首先在VS Code界面上按順序同時按住:ctrl+shift+P,在彈出的窗口中輸入C/C++ Edit Configurations(JSON)查找相關JSON配置文件,在列表中點擊后會自動在VS Code中打開這個配置文件:
{
"configurations": [
{
"name": "Linux",
"includePath": [
"${workspaceFolder}/**"
],
"defines": [],
"compilerPath": "/usr/bin/gcc",
"cStandard": "gnu17",
"cppStandard": "c++11",
"intelliSenseMode": "linux-gcc-x64"
}
],
"version": 4
}
我們所需要做的工作就是,在這個includePath中把相關的路徑都加上,比如我這邊添加的路徑是以下3個:
{
"configurations": [
{
"name": "Linux",
"includePath": [
"${workspaceFolder}/**",
"/usr/include/python3.9/",
"/usr/lib/python3.9/",
"/usr/include/python3.9/cpython/"
],
"defines": [],
"compilerPath": "/usr/bin/gcc",
"cStandard": "gnu17",
"cppStandard": "c++11",
"intelliSenseMode": "linux-gcc-x64"
}
],
"version": 4
}
添加后,include <Python.h>就不會顯示報錯了。
行業潛規則,我們先用C++來調用一個Python的打印函數,輸出Hello World試試:
// cp.cpp
#include <Python.h>
int main(int argc, char *argv[]) {
Py_Initialize();
PyRun_SimpleString("print('hello world')
");
Py_Finalize();
return 0;
}
這里需要注意的是一個運行方式,我們是用g++來進行編譯的,但是g++默認是找不到我們剛才在IDE中所設定的幾個includePath的,因此需要我們手動在編譯的時候加上幾個參數。這些參數其實也可以運行python3.9-config去一個一個查看,這里我們直接推薦一種可以運行成功的參數,其中最重要的是-I和-l這兩個路徑一定要包含:
dechin@ubuntu2004:~/projects/gitlab/dechin/$ g++ -o cpy cp.cpp -lm -std=c++11 -I/usr/include/python3.9/ -lpython3.9 dechin@ubuntu2004:~/projects/gitlab/dechin/$ ll 總用量 4697388 drwxrwxr-x 2 dechin dechin 4096 5月 20 17:10 ./ drwxrwxr-x 8 dechin dechin 4096 5月 19 15:32 ../ -rw-rw-r-- 1 dechin dechin 152 5月 20 17:04 cp.cpp -rwxrwxr-x 1 dechin dechin 16776 5月 20 17:10 cpy*
運行完成后,就會在當前目錄下生成一個剛才指定的名字cpy的一個可執行文件,如果是windows系統,則會生成一個cpy.exe的文件。讓我們執行這個文件:
dechin@ubuntu2004:~/projects/gitlab/dechin/$ ./cpy hello world
成功打印Hello World,又離成功更近了一步。
在C++中如果我們想分割一個字符串,雖然說也是可以實現的,但是應該沒有比Python中執行一個string.split()更加方便快捷的方案了,因此我們測試一個用C++調用Python的split函數的功能。
一開始我們是寫了這樣一個簡單的案例,用PyImport_ImportModule方法去調用pysplit這個python模塊:
// cp.cpp
#include <Python.h>
#include <iostream>
using namespace std;
int main(int argc, char *argv[])
{
Py_Initialize();
if (!Py_IsInitialized())
{
cout << "Initialize failed!" << endl;
return 0;
}
PyObject* pModule = NULL;
PyObject* pFunc;
PyRun_SimpleString("import os");
PyRun_SimpleString("os.system('pwd')");
pModule = PyImport_ImportModule("pysplit");
if (pModule == NULL)
{
cout << "Module Not Found!" << endl;
}
// pFunc = PyObject_GetAttrString(pModule, "sp");
// PyObject* args = Py_BuildValue("s", "Test String Hello Every One !");
// PyObject* pRet = PyObject_CallObject(pFunc, args);
string cList[10];
// PyArg_Parse(pRet, "[items]", &cList);
cout << "res:" << cList << endl;
Py_Finalize();
return 0;
}
對應的Python模塊的內容為:
# pysplit.py
def sp(string):
return string.split()
這是一個非常簡單的函數,但是我們在調用的時候就直接返回了一個錯誤:
dechin@ubuntu2004:~/projects/gitlab/dechin/$ g++ -o cpy cp.cpp -lm -std=c++11 -I/usr/include/python3.9/ -lpython3.9 && ./cpy ['pysplit.py', 'cpy', 'cp.cpp'] Module Not Found! res:0x7ffc622ae900
這個錯誤是說,找不到pysplit這個模塊。但是我們同時借助于PyRun_SimpleString調用了Python中的os庫,執行了一個查看路徑和當前路徑下文件的功能,我們發現這個C++文件和需要引入的pysplit.py其實是在同一個路徑下的,這就很奇怪了沒有導入成功。
經過一番的資料查詢,最后發現,即使是在相同的路徑下,也需要通過Python的sys將當前目錄添加到系統路徑中,才能夠識別到這個模塊,同樣也是使用PyRun_SimpleString的函數:
// cp.cpp
#include <Python.h>
#include <iostream>
using namespace std;
int main(int argc, char *argv[])
{
Py_Initialize();
if (!Py_IsInitialized())
{
cout << "Initialize failed!" << endl;
return 0;
}
PyObject* pModule = NULL;
PyObject* pFunc;
PyRun_SimpleString("import sys");
PyRun_SimpleString("sys.path.append('./')");
pModule = PyImport_ImportModule("pysplit");
if (pModule == NULL)
{
cout << "Module Not Found!" << endl;
}
pFunc = PyObject_GetAttrString(pModule, "sp");
PyObject* args = Py_BuildValue("s", "Test String Hello Every One !");
PyObject* pRet = PyObject_CallObject(pFunc, args);
string cList[10];
// PyArg_Parse(pRet, "[items]", &cList);
cout << "res:" << cList << endl;
Py_Finalize();
return 0;
}
這個也可以理解,Python中的函數調用,輸入參數都被打包成了一個tuple格式,比如**args,而類似**kwargs則是打包成一個字典格式,類似的功能在這篇博客中有所介紹。
上面的問題,在StackOverFlow上有一個類似的情況,有一個回答解決了這個問題,解決方案是,用PyObject_CallFunctionObjArgs來替代PyObject_CallObject去實現函數調用命令,相關代碼如下:
// cp.cpp
#include <Python.h>
#include <iostream>
using namespace std;
int main(int argc, char *argv[])
{
Py_Initialize();
if (!Py_IsInitialized())
{
cout << "Initialize failed!" << endl;
return 0;
}
PyObject* pModule = NULL;
PyObject* pFunc;
PyRun_SimpleString("import sys");
PyRun_SimpleString("sys.path.append('./')");
pModule = PyImport_ImportModule("pysplit");
if (pModule == NULL)
{
cout << "Module Not Found!" << endl;
}
pFunc = PyObject_GetAttrString(pModule, "sp");
PyObject* args = Py_BuildValue("s", "Test String Hello Every One !");
PyObject* pRet = PyObject_CallFunctionObjArgs(pFunc, args, NULL);
int size = PyList_Size(pRet);
cout << "List size is: " << size << endl;
for(int i=0;i<size;i++)
{
PyObject* cRet = PyList_GET_ITEM(pRet, i);
char* s;
PyArg_Parse(cRet, "s", &s);
cout << "The " << i << "th term is: " << s << endl;
}
Py_Finalize();
return 0;
}
最后,因為從Python中獲取的是一個List格式的數據,因此我們首先需要用PyList_GET_ITEM去逐項提取,然后用PyArg_Parse將提取出來的元素保存到一個C++的char字符串中,執行結果如下:
dechin@ubuntu2004:~/projects/gitlab/dechin/$ g++ -o cpy cp.cpp -lm -std=c++11 -I/usr/include/python3.9/ -lpython3.9 && ./cpy List size is: 6 The 0th term is: Test The 1th term is: String The 2th term is: Hello The 3th term is: Every The 4th term is: One The 5th term is: !
讀到這里,這篇“如何在C++中調用Python”文章已經介紹完畢,想要掌握這篇文章的知識點還需要大家自己動手實踐使用過才能領會,如果想了解更多相關內容的文章,歡迎關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。