首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> VC >> VC 教學

C++ 擴展與嵌入 Python 完全指南:雙向互調實戰

     編輯:VC 教學

Python 以簡潔語法、豐富標準庫、跨平台特性成為理想腳本語言。本文詳細講解如何在 C++ 應用中**嵌入 Python 解釋器**(調用 Python 函數、獲取變量)與**編寫 C++ 擴展模塊**(供 Python 調用 C++ 功能),實現雙向無縫互操作。

核心需求場景

C++ 應用通過插件機制實現功能擴展,傳統方案使用動態鏈接庫(DLL),修改需重新編譯鏈接。改用 Python 腳本作為插件,可熱加載、即時修改;但部分底層操作(硬件訪問、高性能計算)仍需 C++ 實現,再暴露給 Python 調用。因此需要雙向橋接:

  1. 嵌入:C++ 主程序初始化 Python,調用腳本函數、讀取變量;
  2. 擴展:編寫 C++ 動態庫,導出函數供 import 後的 Python 代碼調用。

關鍵 Python/C API 速查

API作用示例
Py_Initialize()初始化解釋器,必須最先調用Py_Initialize();
PyRun_SimpleString()執行一段 Python 代碼字符串PyRun_SimpleString("print('Hello')");
PyImport_ImportModule()導入 .py 模塊,返回模塊對象pmod = PyImport_ImportModule("mymod");
PyModule_GetDict()獲取模塊的 __dict__(命名空間字典)pdict = PyModule_GetDict(pmod);
PyDict_GetItemString()從字典取出函數/變量對象pfunc = PyDict_GetItemString(pdict, "myfunc");
PyObject_CallObject()調用可調用對象(函數/類)presult = PyObject_CallObject(pfunc, pargs);
PyLong_AsLong()將 Python 整數轉為 C longlong val = PyLong_AsLong(presult);
Py_Finalize()關閉解釋器,釋放資源Py_Finalize();

嵌入 Python 完整流程示例

#include <Python.h>
#include <iostream>

int main() {
    Py_Initialize();  // 1. 初始化

    // 2. 執行簡單腳本
    PyRun_SimpleString("import sys");
    PyRun_SimpleString("sys.path.append('./scripts')");

    // 3. 導入自定義模塊
    PyObject* pModule = PyImport_ImportModule("business_logic");
    if (!pModule) { PyErr_Print(); return -1; }

    // 4. 獲取模塊字典與目標函數
    PyObject* pDict = PyModule_GetDict(pModule);
    PyObject* pFunc = PyDict_GetItemString(pDict, "process_data");
    if (!pFunc || !PyCallable_Check(pFunc)) { /* 錯誤處理 */ }

    // 5. 構造參數元組 (int, string)
    PyObject* pArgs = PyTuple_New(2);
    PyTuple_SetItem(pArgs, 0, PyLong_FromLong(42));
    PyTuple_SetItem(pArgs, 1, PyUnicode_FromString("test"));

    // 6. 調用並獲取返回值
    PyObject* pResult = PyObject_CallObject(pFunc, pArgs);
    long result = PyLong_AsLong(pResult);
    std::cout << "Python returned: " << result << std::endl;

    // 7. 清理
    Py_DECREF(pArgs); Py_DECREF(pModule); Py_DECREF(pResult);
    Py_Finalize();
    return 0;
}

編寫 C++ 擴展模塊(供 Python import)

核心步驟:定義靜態 PyMethodDef 陣列描述導出函數 → 實現 C 函數(簽名固定為 PyObject* func(PyObject*, PyObject*)) → 定義模塊定義結構 PyModuleDef → 實現 PyInit_modname() 入口函數。

// myext.cpp
#include <Python.h>

static PyObject* myext_add(PyObject* self, PyObject* args) {
    long a, b;
    if (!PyArg_ParseTuple(args, "ll", &a, &b)) return NULL;
    return PyLong_FromLong(a + b);
}

static PyMethodDef MyExtMethods[] = {
    {"add", myext_add, METH_VARARGS, "Add two integers."},
    {NULL, NULL, 0, NULL}
};

static struct PyModuleDef myextmodule = {
    PyModuleDef_HEAD_INIT, "myext", NULL, -1, MyExtMethods
};

PyMODINIT_FUNC PyInit_myext(void) {
    return PyModule_Create(&myextmodule);
}

編譯為 myext.pyd (Windows) 或 myext.so (Linux),放入 Python 路徑,即可 import myext; print(myext.add(3,5))。

進階主題與最佳實踐

  • 引用計數管理:嚴格遵循 Py_INCREF/Py_DECREF,防止內存洩漏或崩潰;
  • 異常傳播:C++ 端檢查 PyErr_Occurred(),Python 端捕獲 C++ 拋出的異常需編寫包裝層;
  • GIL 鎖:多線程嵌入時需手動獲取/釋放 GIL(PyGILState_Ensure/Release);
  • 現代替代方案:推薦使用 pybind11、Boost.Python 或 nanobind 自動化綁定,大幅減少樣板代碼。

掌握 C/C++ 與 Python 的雙向互調,能將 C++ 的高性能與 Python 的靈活開發效率完美結合,是構建可擴展、可腳本化高性能系統的關鍵技能。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved