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

C++ 嵌入與擴展 Python 完整實戰指南

     編輯:關於C++

Python 語言特性概覽

Python 是一種語法簡潔、功能強大的解釋型程式語言,擁有高效的高層資料結構,並原生支援物件導向程式設計。它既適合快速應用開發,也能勝任大型商業級專案的構建。Python 免費開源、跨平台可移植,只要避免使用作業系統專屬特性,程式碼即可在各主流平台無縫運行。

Python 標準庫極其龐大,涵蓋圖形介面、檔案處理、多媒體、正則表達式、單元測試、多執行緒、資料庫、網路通訊、Web 瀏覽、CGI、FTP、電子郵件、XML/HTML 解析、密碼系統等領域。除了標準庫,還有 wxPython、Twisted、Python 圖形庫等大量高質量第三方庫可用。

核心需求:雙向互操作

在某 C++ 應用中,我們使用插件機制實現統一介面功能。為了避免需求變更時頻繁重新編譯動態連結庫,決定以 Python 腳本替代傳統 DLL 插件。雖然 Python 功能強大,但部分作業系統底層操作仍需 C++ 實現再供 Python 調用。因此核心需求有二:

  1. 嵌入 Python:在 C++ 程式中初始化解釋器、呼叫 Python 函式、獲取變數值。
  2. 擴展 Python:用 C++ 編寫擴展模組(DLL),讓 Python 程式能呼叫 C++ 開發的功能函式。

常用 Python/C API 速查與範例

以下 API 為實戰中常用子集,詳細說明請參考官方文檔。

環境準備

在 Visual Studio 中建立主控台專案,包含 <Python.h>,並在 main 函式中編寫程式碼。

// 定義常用變數
char *cstr;
PyObject *pstr, *pmod, *pdict;
PyObject *pfunc, *pargs;

關鍵 API 說明

  1. Py_Initialize():初始化 Python 解釋器,使用其他 API 前必須呼叫。
  2. PyRun_SimpleString(const char*):在 __main__ 命名空間執行 Python 程式碼片段。
    PyRun_SimpleString("from time import time,ctime\n"
        "print 'Today is',ctime(time())\n");
    
  3. PyImport_ImportModule(char*):匯入 Python 模組(如 mymod.py),相當於 __import__()。
  4. PyModule_GetDict(PyObject*):取得模組的命名空間字典(__dict__)。
  5. PyRun_String(...):在指定全域/區域字典中執行程式碼並回傳結果物件。
  6. PyArg_Parse(PyObject*, char*, ...):將 Python 物件解析為 C 類型資料(如字串)。
  7. PyObject_GetAttrString(PyObject*, char*):取得模組中的屬性或函式物件(相當於 o.attr_name)。
  8. Py_BuildValue(char*, ...):將 C 類型資料構建為 Python 物件(如參數元組)。
  9. PyEval_CallObject(PyObject*, PyObject*):呼叫 Python 函式,傳入參數列表,回傳結果物件。
  10. Py_Finalize():關閉解釋器並釋放資源。

簡化封裝:simplepy.h

為減少樣板程式碼,文中提供 CSimplepy 類別封裝常用操作:

#include "simplepy.h"
CSimplepy py;
py.ImportModule("mymod");
std::string str = py.CallObject("transform",
    "this is hjs's test, to uppercase");
printf("%s\n", str.c_str());

編寫 C++ 擴展模組(DLL)供 Python 呼叫

原生 Python/C API 實作

建立 Win32 DLL 專案 pyUtil,核心程式碼如下:

// pyUtil.cpp
#include <windows.h>
#include <string>
#include <Python.h>

std::string Recognise_Img(const std::string url) {
    return "從dll中返回的數據... : " + url;
}

static PyObject* Recognise(PyObject *self, PyObject *args) {
    const char *url;
    if (!PyArg_ParseTuple(args, "s", &url)) return NULL;
    std::string sts = Recognise_Img(url);
    return Py_BuildValue("s", sts.c_str());
}

static PyMethodDef AllMyMethods[] = {
    {"Recognise", Recognise, METH_VARARGS},
    {NULL, NULL}
};

extern "C" __declspec(dllexport) void initpyUtil() {
    Py_InitModule("pyUtil", AllMyMethods);
}

Python 端呼叫:

import pyUtil
result = pyUtil.Recognise("input url of specific data")
print "the result is: " + result

使用 Boost.Python 大幅簡化開發

若引入 Boost.Python,同樣功能只需極少程式碼(需連結 boost_python.lib、執行期需 boost_python.dll):

#include <string>
#include <boost/python.hpp>
using namespace boost::python;
#pragma comment(lib, "boost_python.lib")

std::string strtmp;
char const* Recognise(const char* url) {
    strtmp = "從dll中返回的數據... : ";
    strtmp += url;
    return strtmp.c_str();
}

BOOST_PYTHON_MODULE(pyUtil) {
    def("Recognise", Recognise);
}

測試環境與參考資源

  • 測試環境:Windows XP + VS.NET 2003 + Python 2.4 + Boost 1.33
  • 官方文檔:Python 2.4.1 CHM(安裝目錄 Doc\Python24.chm)
  • 推薦書籍:《Python Programming for the Absolute Beginner》、《Programming Python 2nd Ed.》、《Python Programming on Win32》
  • Python 官網:www.python.org;Boost 官網:www.boost.org

附件原始碼

附件 1:text.txt
this is test text in text.txt.
附件 2:mymod.py
import string
message = 'original string'
message = message + message
msg_error = ""
try:
    text_file = open("text.txt", "r")
    whole_thing = text_file.read()
    print whole_thing
    text_file.close()
except IOError, (errno, strerror):
    print "I/O error(%s): %s" % (errno, strerror)

def transform(input):
    return string.upper(input)

def change_msg(nul):
    global message
    message = 'string changed'
    return message

def r_file(nul):
    return whole_thing

def get_msg(nul):
    return message
附件 3:simplepy.h 完整定義
#ifndef _SIMPLEPY_H_
#define _SIMPLEPY_H_
#include <string>
using std::string;
#include <Python.h>

class CSimplepy {
public:
    CSimplepy() {
        Py_Initialize();
        pstr=pmod=pdict=pfunc=pargs=NULL;
    }
    virtual ~CSimplepy() { Py_Finalize(); }

    bool ImportModule(const char* mod_name) {
        try {
            pmod = PyImport_ImportModule(const_cast<char*>(mod_name));
            if(!pmod) return false;
            pdict = PyModule_GetDict(pmod);
        } catch(...) { return false; }
        return pmod && pdict;
    }
    int Run_SimpleString(const char* str) {
        return PyRun_SimpleString(const_cast<char*>(str));
    }
    string Run_String(const char* str) {
        char *cstr;
        pstr = PyRun_String(str, Py_eval_input, pdict, pdict);
        if(!pstr) throw "Run_String exception";
        PyArg_Parse(pstr, "s", &cstr);
        return string(cstr);
    }
    string CallObject(const char* func_name, const char* parameter) {
        pfunc = PyObject_GetAttrString(pmod, const_cast<char*>(func_name));
        if(!pfunc) throw (string("not found: ")+func_name).c_str();
        pargs = Py_BuildValue("(s)", const_cast<char*>(parameter));
        pstr = PyEval_CallObject(pfunc, pargs);
        if(!pstr) throw "PyEval_CallObject exception";
        char* cstr;
        PyArg_Parse(pstr, "s", &cstr);
        return string(cstr);
    }
protected:
    PyObject *pstr, *pmod, *pdict, *pfunc, *pargs;
};
#endif
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved