C++ 高效能與 Python 靈活腳本能力的結合,是眾多高效能應用(遊戲邏輯、科學計算、自動化測試、AI 推理預處理)的首選架構。本文提供完整的 C/C++ 嵌入 Python 實作範例,涵蓋解譯器初始化、模組載入、函數呼叫、引數傳遞、回傳值轉換與參考計數管理,助你快速構建穩健的雙語言系統。
Py_Initialize():初始化 Python 解譯器(建立 GIL、載入內建模組)。PyImport_ImportModule() / PyImport_Import():載入 Python 模組(.py/.pyc/.so/.pyd)。PyObject_GetAttrString() / PyDict_GetItemString():取得模組屬性(函數/類別/變數)。PyTuple_New() + PyTuple_SetItem() / Py_BuildValue():建構呼叫引數元組。PyObject_CallObject() / PyObject_Call():呼叫可呼叫物件。PyLong_AsLong() / PyFloat_AsDouble() / PyUnicode_AsUTF8():回傳值轉換為 C 型別。Py_DECREF() / Py_XDECREF():釋放 Python 物件參考(極關鍵,防記憶體洩漏)。Py_FinalizeEx():關閉解譯器(釋放所有資源)。test1.py 中的 Add(a, b) 函數
def Add(a, b):
return a + b
#include
#include
int main() {
// 1. 初始化解譯器
Py_Initialize();
if (!Py_IsInitialized()) {
std::cerr << "Python 初始化失敗\n";
return -1;
}
// 2. 載入模組 (建議使用 PyImport_ImportModule,直接接受 UTF-8 字串)
PyObject* pModule = PyImport_ImportModule("test1");
if (!pModule) {
PyErr_Print(); // 列印 Python 異常堆疊
std::cerr << "無法載入模組 test1\n";
Py_FinalizeEx();
return -1;
}
std::cout << "成功載入模組 test1\n";
// 3. 取得函數物件
PyObject* pFunc = PyObject_GetAttrString(pModule, "Add");
if (!pFunc || !PyCallable_Check(pFunc)) {
std::cerr << "找不到可呼叫的 Add 函數\n";
Py_XDECREF(pFunc);
Py_DECREF(pModule);
Py_FinalizeEx();
return -1;
}
// 4. 準備引數: Add(10, 20)
// 方法 A: 手動建構 Tuple (教學用)
PyObject* pArgs = PyTuple_New(2);
PyTuple_SetItem(pArgs, 0, PyLong_FromLong(10)); // 偷取參考,無需 DECREF
PyTuple_SetItem(pArgs, 1, PyLong_FromLong(20));
// 方法 B: 使用 Py_BuildValue 簡化 (推薦實務用)
// PyObject* pArgs = Py_BuildValue("(ii)", 10, 20); // "(ii)" 表兩個 int 組成 tuple
// 5. 呼叫函數
PyObject* pResult = PyObject_CallObject(pFunc, pArgs);
if (!pResult) {
PyErr_Print();
std::cerr << "函數呼叫失敗\n";
} else {
// 6. 轉換回傳值
long result = PyLong_AsLong(pResult);
if (result == -1 && PyErr_Occurred()) {
PyErr_Print();
} else {
std::cout << "Add(10, 20) = " << result << "\n"; // 輸出 30
}
Py_DECREF(pResult);
}
// 7. 釋放資源 (逆序,嚴格配對)
Py_DECREF(pArgs);
Py_DECREF(pFunc);
Py_DECREF(pModule);
// 8. 關閉解譯器
Py_FinalizeEx();
return 0;
}
PyImport_ImportModule、PyObject_GetAttrString、PyTuple_New、PyLong_FromLong、Py_BuildValue、PyObject_CallObject 回傳「新參考」,呼叫者負責 Py_DECREF。PyTuple_SetItem、PyList_SetItem、PyDict_SetItem 「偷取」傳入物件參考,呼叫者不可再 DECREF。PyTuple_GetItem、PyList_GetItem、PyDict_GetItem 回傳「借用參考」,呼叫者不可 DECREF,若需持有需自行 Py_INCREF。nullptr,適用於可能為空的指標。
PyObject* pResult = PyObject_CallObject(pFunc, pArgs);
if (!pResult) {
// Python 例外已設定,需檢查並處理
if (PyErr_ExceptionMatches(PyExc_TypeError)) {
// 特定例外處理
}
PyErr_Print(); // 列印預設 traceback 到 stderr
PyErr_Clear(); // 清除例外狀態 (若已處理)
}
PyGILState_STATE state = PyGILState_Ensure(); ...呼叫 Python API... PyGILState_Release(state);。Py_BEGIN_ALLOW_THREADS ...計算... Py_END_ALLOW_THREADS。const char*。PyUnicode_FromString("中文") 或 PyUnicode_FromStringAndSize()。const char* utf8 = PyUnicode_AsUTF8(pUnicodeObj); (回傳借用指標,物件存活期間有效)。
class PyObjectPtr {
PyObject* ptr_;
public:
PyObjectPtr(PyObject* p = nullptr) : ptr_(p) {}
~PyObjectPtr() { Py_XDECREF(ptr_); }
PyObjectPtr(const PyObjectPtr&) = delete;
PyObjectPtr& operator=(const PyObjectPtr&) = delete;
PyObjectPtr(PyObjectPtr&& other) noexcept : ptr_(other.ptr_) { other.ptr_ = nullptr; }
PyObjectPtr& operator=(PyObjectPtr&& other) noexcept {
Py_XDECREF(ptr_); ptr_ = other.ptr_; other.ptr_ = nullptr; return *this;
}
PyObject* get() const { return ptr_; }
PyObject* release() { PyObject* p = ptr_; ptr_ = nullptr; return p; }
operator bool() const { return ptr_ != nullptr; }
};
// 使用範例
PyObjectPtr pModule(PyImport_ImportModule("test1"));
PyObjectPtr pFunc(PyObject_GetAttrString(pModule.get(), "Add"));
PyObjectPtr pArgs(Py_BuildValue("(ii)", 10, 20));
PyObjectPtr pResult(PyObject_CallObject(pFunc.get(), pArgs.get()));
// 離開作用域自動釋放,零洩漏
手寫 Python C API 繁瑣且易錯。pybind11 提供純標頭檔、C++11 風格綁定,自動處理參考計數、型別轉換、例外傳播、GIL 管理。
// pybind11 綁定範例 (編譯為 .so/.pyd 供 Python import)
#include
namespace py = pybind11;
int add(int a, int b) { return a + b; }
PYBIND11_MODULE(my_module, m) {
m.def("add", &add, "Add two integers");
}
反向嵌入(C++ 執行 Python 程式碼)pybind11 亦支援 py::exec()、py::module::import() 等高層 API,生產環境強烈建議採用。
$(Python_INCLUDE_DIRS) (如 /usr/include/python3.11 或 C:\Python311\include)python3.11 (Linux/macOS) 或 python311.lib (Windows)find_package(Python3 REQUIRED COMPONENTS Interpreter Development) → target_link_libraries(myapp PRIVATE Python3::Python)C++ 嵌入 Python 核心在於:正確的參考計數管理、完善的異常處理、GIL 感知的多執行緒設計。手寫 C API 適合學習與極簡依賴場景,實務專案請直接採用 pybind11 或 nanobind,以現代 C++ 風格享受零成本抽象的綁定體驗。