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

C++ 調用 Python 完整實戰:嵌入式解釋器與系統調用雙方案

     編輯:關於C++

兩種調用方式對比

方式原理優點缺點
嵌入式 Python (Python.h)進程內加載 Python 解釋器高性能、直接數據交換、共享內存編譯鏈接複雜、版本綁定、GIL 限制
系統調用 (popen/system)啟動獨立 Python 進程簡單、隔離性好、版本無關進程開銷大、IPC 需序列化

方案一:嵌入式 Python (Python.h)

編譯鏈接關鍵

  • 包含目錄:-I/usr/include/python3.x (或 python3-config --includes)
  • 鏈接庫:-lpython3.x (或 python3-config --ldflags --embed)
  • C++11:-std=c++11

核心 API 流程

  1. Py_Initialize() 初始化解釋器
  2. PyRun_SimpleString("import sys; sys.path.append('...')") 設置模塊路徑
  3. PyImport_ImportModule("module_name") 導入模塊
  4. PyObject_GetAttrString(module, "func_name") 獲取函數對象
  5. 構造參數元組 PyTuple_Pack / Py_BuildValue
  6. PyObject_CallObject(func, args) 調用
  7. 結果轉換:PyLong_AsLong, PyUnicode_AsUTF8 等
  8. Py_Finalize() 清理

完整封裝類 (util.h/util.cpp)


// util.h
#ifndef UTIL_H
#define UTIL_H
#include 
#include 

void init_log(const char* argv);
char* exe_command(const char* cmd); // 系統調用備選
void exe_py(const std::string& module, const std::string& func, const std::vector& args);
#endif

// util.cpp
#include "util.h"
#include 
#include 
#include 
#include 
#include 

#define PYTHON_LIB_PATH "~/.jumbo/lib/python2.7"
#define PYTHON_BIN_PATH "~/.jumbo/bin/python2.7"
#define MAX_LENGTH 2048

void init_log(const char* argv) {
    if (!getenv("GLOG_logtostderr")) google::LogToStderr();
    google::InstallFailureSignalHandler();
    google::InitGoogleLogging(argv);
    LOG(INFO) << "Log initialized";
}

char* exe_command(const char* cmd) {
    FILE* fp = popen(cmd, "r");
    if (!fp) { LOG(FATAL) << "popen failed: " << cmd; return nullptr; }
    char* buf = (char*)malloc(MAX_LENGTH);
    fread(buf, 1, MAX_LENGTH-1, fp);
    buf[strlen(buf)-1] = '\0'; // 去掉換行
    pclose(fp);
    return buf;
}

void exe_py(const std::string& module, const std::string& func, const std::vector& args) {
    // 構造命令行調用(系統調用版,簡單可靠)
    std::string args_str;
    for (auto& a : args) args_str += "\"" + a + "\",";
    std::string cmd = "LD_LIBRARY_PATH=" PYTHON_LIB_PATH ":$LD_LIBRARY_PATH " PYTHON_BIN_PATH " -c "
        "'import sys; sys.path.append(\"pyfiles\"); import " + module + "; ret = " + module + "." + func + "([" + args_str + "])'";
    LOG(INFO) << "Executing: " << cmd;
    char* res = exe_command(cmd.c_str());
    // 解析 res...
    free(res);
}

// 真正嵌入式調用版本(需鏈接 libpython)
void exe_py_embedded(const std::string& module, const std::string& func, const std::vector& args) {
    Py_Initialize();
    PyRun_SimpleString("import sys");
    PyRun_SimpleString("sys.path.append(\"./pyfiles\")");

    PyObject* pModule = PyImport_ImportModule(module.c_str());
    if (!pModule) { PyErr_Print(); Py_Finalize(); return; }

    PyObject* pFunc = PyObject_GetAttrString(pModule, func.c_str());
    if (!pFunc || !PyCallable_Check(pFunc)) { PyErr_Print(); Py_DECREF(pModule); Py_Finalize(); return; }

    PyObject* pArgs = PyTuple_New(args.size());
    for (size_t i = 0; i < args.size(); i++)
        PyTuple_SetItem(pArgs, i, PyUnicode_FromString(args[i].c_str()));

    PyObject* pResult = PyObject_CallObject(pFunc, pArgs);
    if (pResult) {
        // 處理結果...
        Py_DECREF(pResult);
    } else PyErr_Print();

    Py_DECREF(pArgs); Py_DECREF(pFunc); Py_DECREF(pModule);
    Py_Finalize();
}

方案二:系統調用 (popen) - 代碼見上 exe_command

適合簡單場景、腳本隔離、避免 GIL 衝突。注意路徑、環境變量、參數轉義。

項目結構示例

project/
├── include/util.h
├── src/util.cpp
├── main.cpp
├── pyfiles/          # Python 模塊目錄
│   ├── printargs.py
│   └── __init__.py
├── Makefile          # 關鍵:-I -lpython3.x -std=c++11
└── build/

關鍵坑點

  • Python 2 vs 3 API 差異(PyString vs PyUnicode)
  • 引用計數管理:Py_INCREF/DECREF 嚴格配對
  • 異常處理:PyErr_Occurred(), PyErr_Print()
  • 多線程:需 PyGILState_Ensure/Release 管理 GIL
  • 路徑問題:sys.path.append 必須在導入前
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved