C/C++與Python混合編程
Python與C++混合編程可以實現兩種語言的優勢結合,C++的程序性能很高且支持強大的系統調用能力,Python則生態豐富且開發效率高。本章將基于Python3講述Python與C++混合編程的技術。
1. Python簡介
1.1. 什么是Python?
Python是一種高級編程語言,具有簡潔易讀的語法和強大的功能。它于 1991 年由 Guido van Rossum 首次發布,快速發展成為一種廣泛使用的編程語言。它是一種動態腳本語言,崇尚優美、清晰、簡單的語法。
Python具有以下一些特性:
- 跨平臺:語言級別跨平臺,幾乎可以在各個平臺間無縫切換,如 Windows、macOS 和 Linux等。
- 生態豐富:擁有眾多的標準庫和第三方庫,且擁有優秀的包管理機制。
- 多范式編程: 支持多種編程范式,包括面向對象編程、過程式編程和函數式編程。
- 應用廣泛:Python 被廣泛應用于科學計算、數據分析、人工智能、網絡開發、自動化測試等領域。
1.2. 常見數據類型
Python是一種動態語言,變量的定義不需要在前面加類型說明,而且不同類型之間可以方便地相互轉換。如下示例代碼:
a = "124"
print("a:", a, "type:", type(a))
b = int(a)
print("b:", b, "type:", type(b))
執行結果:
a: 124 type: <class 'str'>
b: 124 type: <class 'int'>
Python3中有六個標準的數據類型:
- Numbers(數字)
- String(字符串)
- List(列表)
- Tuple(元組)
- Dictionary(字典)
- Set(集合)
其中List、Tuple、Dictionary、Set為容器。Python支持四種不同的數字類型:int(有符號整型)、float(浮點型)、bool(布爾型)、complex(復數)。(說明:Python3中已去除long類型,與int類型合并)。
Python具有以下常用的數據類型轉換函數:
| 函數 | 描述 |
|---|---|
| int(x [,base]) | 將x轉換為一個整數。base為可選參數,表示進制數,默認十進制。 |
| float(x) | 將x轉換到一個浮點數 |
| complex(real [,imag]) | 創建一個復數。imag為可選參數,表示虛數部分 |
| str(x) | 將對象 x 轉換為字符串 |
| tuple(s) | 將序列 s 轉換為一個元組 |
| list(s) | 將序列 s 轉換為一個列表 |
| set(s) | 轉換為可變集合 |
| dict(d) | 創建一個字典。d 必須是一個 (key, value)元組序列。 |
| frozenset(s) | 轉換為不可變集合 |
| chr(x) | 將一個整數轉換為一個字符 |
| ord(x) | 將一個字符轉換為它的整數值 |
| hex(x) | 將一個整數轉換為一個十六進制字符串 |
| oct(x) | 將一個整數轉換為一個八進制字符串 |
1.3. 環境說明
本章所有的示例代碼都是使用Python3進行編寫,具體的環境如下:
- 操作系統: Ubuntu 24.04
- Python: 3.12.3
- 開發工具:VSCode
2. 開發環境搭建
2.1. Windows
- 下載最新版本的安裝包,官網下載地址: https://www.python.org/downloads/。
- 點擊安裝包,根據提示一步一步操作即可,只需要注意以下兩點:
- 勾選
Add python.exe to PATH將Python添加到環境變量
![file]()
- 如果要自定義安裝路徑,第一步需要選擇
Customize installation(如上圖所示),然后Advaned options頁面選擇對于的安裝路徑(如下圖)。
![file]()
- 勾選
- 安裝完后打開命令提示符,然后輸入
python -V,如果顯示對應的版本號,則說明安裝成功。
2.2. Linux(Ubuntu)
# 1. 更新軟件包列表
sudo apt update
# 2. 安裝python3
sudo apt install python3
# 3. 驗證python3是否安裝成功,如果顯示對應的版本號則說明安裝成功。
python3 -V
# 4. 安裝python3開發包
sudo apt install python3-dev
# 驗證python3-dev是否安裝成功,如果能看到相關的包信息則說明安裝成功。
dpkg -l | grep python3-dev
2.3. macOS
# 前期準備:安裝Homebrew
# 執行`brew -v`檢查Homebrew是否安裝,如果未安裝,執行以下命令安裝
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
# 1. 更新軟件包列表
brew update
# 2. 安裝python
brew install python
# 3. 驗證版本號,如何顯示對應的版本號則說明安裝成功。
python3 -V
3. Python/C API
3.1. 什么是Python/C API?
Python/C API是Python官方提供的一套API接口,允許開發者使用 C/C++ 語言來擴展Python。這個接口使得開發者可以在 C/C++ 語言中編寫模塊,這些模塊可以被 Python 程序調用,從而執行更高效的計算,或者訪問操作系統級別的資源。Python/C API也支持在C/C++中調用Python的模塊代碼,從而實現跨語言的混合編程。
官方文檔:https://docs.python.org/3.8/c-api/index.html#c-api-index
3.2. C++調用Python模塊
py_math.py:
def add(a: int, b: int):
res = a + b
return res
def sub(a: int, b: int):
return a - b
cpp_call_python.cpp:
#include <Python.h>
#include <cstdint>
#include <iostream>
int32_t add_from_python(int32_t a, int32_t b, int32_t& res)
{
PyObject* pModuleName = PyUnicode_FromString("py_math");
if (!pModuleName)
{
PyErr_Print();
return -1;
}
// 導入模塊
PyObject* pModule = PyImport_Import(pModuleName);
if (!pModule)
{
PyErr_Print();
Py_DECREF(pModuleName);
return -1;
}
/* great_module.great_function */
PyObject* pFunc = PyObject_GetAttrString(pModule, "add");
if (!pFunc || !PyCallable_Check(pFunc))
{
PyErr_Print();
Py_DECREF(pModule);
Py_DECREF(pModuleName);
return -1;
}
// 設置參數
PyObject* pArgs = PyTuple_Pack(
2,
PyLong_FromLong(a),
PyLong_FromLong(b)
);
if (!pArgs)
{
PyErr_Print();
Py_DECREF(pFunc);
Py_DECREF(pModule);
Py_DECREF(pModuleName);
return -1;
}
// 調用函數
PyObject* pValue = PyObject_CallObject(pFunc, pArgs);
if (!pValue)
{
PyErr_Print();
Py_DECREF(pFunc);
Py_DECREF(pModule);
Py_DECREF(pModuleName);
return -1;
}
// 獲取結果
res = PyLong_AsLong(pValue);
Py_DECREF(pValue);
Py_DECREF(pFunc);
Py_DECREF(pModule);
Py_DECREF(pModuleName);
return 0;
}
int main()
{
// 1. 初始化 Python 解釋器
Py_Initialize();
// 2. 調用 Python 代碼
int32_t result = 0;
auto ret = add_from_python(3, 2, result);
if (ret < 0)
{
std::cerr << "add_from_python error ret:" << ret << std::endl;
}
else
{
std::cout << "3 + 2 = " << result << std::endl;
}
// 3. 結束 Python 解釋器
Py_Finalize();
return 0;
}
編譯和執行:
# 編譯
g++ -g cpp_call_python.cpp -o cpp_call_python -I /usr/include/python3.12 -lpython3.12
# 運行
./cpp_call_python
3 + 2 = 5
常見問題和解決策略:
如果出現找不到module的錯誤:ModuleNotFoundError: No module named 'py_math。需要設置PYTHONPATH環境變量,設置方法如下:
# 1. vim打開.zshrc(如果你的SHELL用的是.bashrc,替換成相應的.bashrc)
vim ~/.zshrc
# 2. 在文件末尾添加如下內容
export PYTHONPATH=.:$PYTHONPATH
# 3. 重新加載配置
source ~/.zshrc
上面第2步表示將程序執行的當前目錄加到PYTHONPATH環境變量中。
3.3. Python調用C++模塊
cpp_math.cpp:
#include <Python.h>
#include <cstdint>
#include <iostream>
int32_t add(int32_t a, int32_t b)
{
return a + b;
}
static PyObject* _add(PyObject* self, PyObject* args)
{
int32_t _a;
int32_t _b;
int32_t res;
// 將Python的參數轉換為C++的參數
if (!PyArg_ParseTuple(args, "ii", &_a, &_b))
return NULL;
// 調用add函數
res = add(_a, _b);
// 將C++的返回值轉換為Python的返回值
return PyLong_FromLong(res);
}
static PyMethodDef MathModuleMethods[] = { { "add", _add, METH_VARARGS, "" },
{ NULL, NULL, 0, NULL } };
static struct PyModuleDef MathModule = { PyModuleDef_HEAD_INIT,
"cpp_math", // 模塊名稱
NULL, // 模塊文檔
-1, // 模塊狀態
MathModuleMethods };
// (C擴展)模塊初始化
PyMODINIT_FUNC PyInit_cpp_math(void)
{
// 創建模塊對象
return PyModule_Create(&MathModule);
}
python_call_cpp.py:
from cpp_math import add
print("3 + 2 =", add(3, 2))
編譯和執行:
# 編譯
g++ -fPIC -shared cpp_math.cpp -o cpp_math.so -I /usr/include/python3.12 -lpython3.12
# 運行
python ./python_call_cpp.py
3 + 2 = 5
關鍵代碼說明:
PyArg_ParseTuple的用法
- 第一個參數:是Python的參數列表,是一個Tuple類型。
- 第二個參數:是一個字符串,表示要解析的參數的數據類型,有幾個參數就寫幾個標識符,如:有int和float兩個參數,就寫"if""。數據類型標識和類型的對應關系如下:
- i:表示將參數解析為 int 類型
- f:表示將參數解析為 float 類型
- s:表示將參數解析為 char*(字符串)類型
- O:表示將參數解析為 PyObject* 類型
- 之后的參數:第二個參數之后的是可變參數,填寫對應的參數值或變量。
3.4. 數據類型轉換
Python/C API提供了一系列用于Python和C/C++之間數據類型轉換的函數,它們之間的對應關系如下:
| Python --> C/C++ | C/C++ --> Python | 備注 |
|---|---|---|
| PyLong_AsLong | PyLong_FromLong | 對應C++long類型的轉換 |
| PyLong_AsUnsignedLong | PyLong_FromUnsignedLong | 對應C++ unsigned long類型的轉換 |
| PyUnicode_AsUTF8AndSize | PyUnicode_FromString | 對應C++ const char *類型的轉換 |
| PyFloat_AsDouble | PyFloat_FromDouble | 對應C++ double類型的轉換 |
歷史文章推薦:
大家好,我是陌塵。
IT從業10年+, 北漂過也深漂過,目前暫定居于杭州,未來不知還會飄向何方。
搞了8年C++,也干過2年前端;用Python寫過書,也玩過一點PHP,未來還會折騰更多東西,不死不休。
感謝大家的關注,期待與你一起成長。



浙公網安備 33010602011771號