Python 擁有豐富的科學計算、數據分析、機器學習生態,而 C/C++ 擁有極致性能與系統級能力。通過 C 調用 Python(嵌入式) 與 Python 調用 C/C++(擴展模塊) 兩種模式,可實現「性能核心用 C,業務邏輯用 Python」的最佳工程實踐。本文系統講解嵌入式調用全流程、核心 API、數據轉換與工程化注意事項。
| 組件 | 版本/路徑 | 說明 |
|---|---|---|
| 作業系統 | Ubuntu 14.04 / Linux | 生產環境推薦 CentOS 7+ / Ubuntu 18.04+ |
| 編譯器 | GCC 4.8.2+ | 需支持 C99/C++11 |
| Python | 2.7.6 / 3.x | 新項目強烈建議 Python 3.8+ |
| 頭文件 | Python.h | 通常在 /usr/include/pythonX.Y/ |
| 庫文件 | libpythonX.Y.so | 鏈接時加 -lpythonX.Y |
# C 調用 Python 2.7
gcc -o c_call_python c_call_python.c -I/usr/include/python2.7 -lpython2.7
# C++ 調用 Python 3.8
g++ -o cpp_call_python cpp_call_python.cpp -I/usr/include/python3.8 -lpython3.8 -std=c++11
Py_Initialize(),檢查 Py_IsInitialized()PyRun_SimpleString("import sys; sys.path.append('./')")PyImport_ImportModule("module_name")(模塊名非文件名)PyObject_GetAttrString(pModule, "func_name")PyTuple_New(n) + PyTuple_SetItem 或 Py_BuildValuePyObject_CallObject(pFunc, pArgs)PyLong_AsLong、PyFloat_AsDouble、PyUnicode_AsUTF8 等Py_DECREF 釋放所有 PyObject*Py_Finalize()#include
#include
#include
int main(int argc, char** argv) {
if (argc != 3) {
printf("Usage: %s \n", argv[0]);
return -1;
}
long a = strtol(argv[1], NULL, 10);
long b = strtol(argv[2], NULL, 10);
Py_Initialize();
if (!Py_IsInitialized()) {
fprintf(stderr, "Python 初始化失敗\n");
return -1;
}
// 添加當前目錄到模塊搜索路徑
PyRun_SimpleString("import sys");
PyRun_SimpleString("sys.path.append('.')");
// 導入模塊 math_test (對應 math_test.py)
PyObject* pModule = PyImport_ImportModule("math_test");
if (!pModule) {
PyErr_Print();
fprintf(stderr, "導入模塊失敗\n");
Py_Finalize();
return -1;
}
// 獲取 add_func 函數
PyObject* pFunc = PyObject_GetAttrString(pModule, "add_func");
if (!pFunc || !PyCallable_Check(pFunc)) {
PyErr_Print();
fprintf(stderr, "獲取函數失敗\n");
Py_DECREF(pModule);
Py_Finalize();
return -1;
}
// 構造參數: (a, b)
PyObject* pArgs = Py_BuildValue("(ll)", a, b); // 直接構造元組
if (!pArgs) {
fprintf(stderr, "構造參數失敗\n");
Py_DECREF(pFunc);
Py_DECREF(pModule);
Py_Finalize();
return -1;
}
// 調用
PyObject* pResult = PyObject_CallObject(pFunc, pArgs);
Py_DECREF(pArgs);
if (!pResult) {
PyErr_Print();
fprintf(stderr, "函數調用失敗\n");
Py_DECREF(pFunc);
Py_DECREF(pModule);
Py_Finalize();
return -1;
}
// 解析結果 (Python 3 用 PyLong_AsLong)
long result = PyLong_AsLong(pResult);
printf("%ld + %ld = %ld\n", a, b, result);
// 清理
Py_DECREF(pResult);
Py_DECREF(pFunc);
Py_DECREF(pModule);
Py_Finalize();
return 0;
}
def add_func(a, b):
return a + b
def sub_func(a, b):
return a - b
if __name__ == "__main__":
# 可獨立運行測試
print(add_func(12, 15))
Py_BuildValue(C→Python)與 PyArg_ParseTuple(Python→C)使用相同格式字符串:
| 格式符 | C 類型 | Python 類型 | 說明 |
|---|---|---|---|
i | int | int | 有符號整數 |
l | long | int | 長整數 |
d | double | float | 雙精度浮點 |
f | float | float | 單精度浮點 |
s | char* | str | UTF-8 字符串 |
s# | char*, int | str | 指定長度字符串 |
y | char* | bytes | 字節串 |
O | PyObject* | 任意 | 傳遞現有對象(借用引用) |
N | PyObject* | 任意 | 傳遞現有對象(偷取引用) |
(...) | — | tuple | 構造元組 |
[...] | — | list | 構造列表 |
{...} | — | dict | 構造字典 |
// 無參
Py_BuildValue("") // None
// 單值
Py_BuildValue("i", 42) // 42
Py_BuildValue("s", "hello") // 'hello'
// 元組
Py_BuildValue("(ii)", 1, 2) // (1, 2)
Py_BuildValue("iii", 1, 2, 3) // (1, 2, 3) 多格式自動成元組
// 列表
Py_BuildValue("[ii]", 1, 2) // [1, 2]
// 字典
Py_BuildValue("{s:i, s:i}", "a", 1, "b", 2) // {'a':1, 'b':2}
#include
#include
#include
#include
// 自定義 deleter
struct PyObjectDeleter {
void operator()(PyObject* obj) const {
if (obj) Py_DECREF(obj);
}
};
// 智能指針別名
using PyObjPtr = std::unique_ptr;
// 封裝調用器
class PythonCaller {
public:
PythonCaller() {
Py_Initialize();
if (!Py_IsInitialized()) throw std::runtime_error("Python 初始化失敗");
PyRun_SimpleString("import sys");
PyRun_SimpleString("sys.path.append('.')");
}
~PythonCaller() { Py_Finalize(); }
// 禁用拷貝,啟用移動
PythonCaller(const PythonCaller&) = delete;
PythonCaller& operator=(const PythonCaller&) = delete;
PythonCaller(PythonCaller&&) = default;
PythonCaller& operator=(PythonCaller&&) = default;
template
long callInt(const char* module, const char* func, Args... args) {
PyObjPtr pModule(PyImport_ImportModule(module));
if (!pModule) throwPythonError("導入模塊失敗: " + std::string(module));
PyObjPtr pFunc(PyObject_GetAttrString(pModule.get(), func));
if (!pFunc || !PyCallable_Check(pFunc.get()))
throwPythonError("獲取函數失敗: " + std::string(func));
// 構造參數元組 (C++17 折疊表達式)
PyObjPtr pArgs(Py_BuildValue("(" + std::string(sizeof...(Args), 'l') + ")", args...));
if (!pArgs) throwPythonError("構造參數失敗");
PyObjPtr pResult(PyObject_CallObject(pFunc.get(), pArgs.get()));
if (!pResult) throwPythonError("函數調用異常");
return PyLong_AsLong(pResult.get());
}
private:
void throwPythonError(const std::string& msg) {
PyErr_Print();
throw std::runtime_error(msg);
}
};
// 使用示例
int main() {
try {
PythonCaller py;
long result = py.callInt("math_test", "add_func", 12L, 15L);
printf("Result: %ld\n", result); // 27
} catch (const std::exception& e) {
fprintf(stderr, "Error: %s\n", e.what());
return -1;
}
return 0;
}
PyGILState_Ensure() / PyGILState_Release()Py_BEGIN_ALLOW_THREADS / Py_END_ALLOW_THREADSPyErr_Occurred()PyErr_Print() 輸出堆棧,或 PyErr_Fetch() 獲取異常對象自定義處理Py_BuildValue、PyImport_ImportModule、PyObject_CallObject 返回新引用,必須 Py_DECREFPyTuple_GetItem、PyList_GetItem、PyDict_GetItem 不增加引用計數,不可 DECREFPyTuple_SetItem、PyList_SetItem、PyDict_SetItem 偷取參數引用,調用後不可再 DECREF 參數sys.path.insert(0, '/abs/path')PyUnicode_FromString/PyUnicode_AsUTF8# -*- coding: utf-8 -*-| 方案 | 適用場景 | 優點 | 缺點 | 學習曲線 |
|---|---|---|---|---|
| C 調用 Python (嵌入式) | 宿主程序是 C/C++,需動態腳本化、插件系統、調用 AI 模型 | 無需編譯 Python 擴展,腳本熱更新,開發靈活 | 啟動開銷大,GIL 限制併發,調試困難 | 中 |
| Python 調用 C (擴展模塊) | 性能熱點、硬件驅動、底層庫封裝 | 原生性能,無 GIL 限制(釋放後),Python 風格調用 | 需編寫 C 擴展代碼,編譯部署複雜,內存管理易出錯 | 高 |
| ctypes / cffi | 快速調用現有 .so/.dll,無需寫 C 擴展代碼 | 純 Python 實現,無編譯步驟,跨平台 | 性能略低,複雜數據結構映射繁瑣 | 低 |
| Cython | Python 代碼加速、編寫擴展模塊 | 語法接近 Python,自動生成 C 代碼,性能接近原生 | 需編譯工具鏈,調試相對困難 | 中 |
| pybind11 (C++11+) | 現代 C++ 綁定 Python,頭文件-only | 語法優雅,自動類型轉換,支持 C++17 特性 | 需 C++11 編譯器,模板編譯慢 | 中低 |
multiprocessing 繞過 GILfind_package(Python3 REQUIRED) 管理編譯鏈接,配合 pybind11 的 pybind11_add_module混合編程是系統工程能力的體現,核心在於邊界劃分清晰、數據轉換零拷貝、生命週期精確可控。掌握本文核心 API 與工程化模式,即可在性能與生產力間游刃有餘。