Extending(擴展)&Embeding(嵌入)python


      跨語言相互調用,一直是不同編程語言間代碼交互Interop的難題,微軟一直致力於給C++與C#找個理想的”翻譯“,這么多年在語法語義(當然還應該包含編譯器)和ABI(應用二進制接口)層面做了不少嘗試,進而產生了C++\CLI,C++\CX和COM等技術產物,但這些產物如同現實中自然語言翻譯一樣,並不算太完美(java同其他語言交互的機制不太了解)。在這點上Python似乎把問題解決的很好,這也就是為什么Python會叫做膠水語言。正是由於python的這一特性,所以它被廣泛用於自動化測試中。

python與其他語言交互:

  1.使用ctypes 模塊調用 C 動態庫:如果被測試模塊是以C語言編寫,可以用Ctypes調用動態鏈接庫,好處在於不用進行額外的開發,可以直接使用編譯好的動態庫。 ctypes 提供了完整的 C 類型封裝,也支持自定義類型,大大減少在調用過程中的工作量。但C++的改名機制使得python調用C++編譯的動態鏈接庫很麻煩。

  2.使用原生態的Python 的擴展和嵌入( Extending &Embedding)機制:Python 提供了一套完整的Extending框架來使用 C/C++ 編寫擴展庫,可以很靈活的開發 C/C++ 擴展模塊。這種方法的缺點是工作量比較大,需要為每一個方法編寫接口(但通過 SWIG可以降低工作量高效的調用動態鏈接庫)。通過Embedding機制則可以使用C/C++調用python代碼進行交互。

      (Note:SWIG 是一種簡化腳本語言與 C/C++ 接口的開發工具,通過包裝和編譯 C 語言程序來達到與腳本語言通訊目的的工具。它正是基於 Python 的擴展機制,自動生成接口文件,再編譯成可以被 Python 調用的動態庫擴展模。目前不打算去了解SWIG,寫下來備忘。)

  3.Boost.Python:  Boot Python是一個為實現C++與Python無縫交互而編寫的類庫。它可以不借助任何工具僅憑C++編譯器使C++類方法和對象快速無縫的暴露給Python,而目前(2.0版本)C++代碼調用python模塊還需要借助Python/C API不過已經很大程度減少了工作量。這個類庫使用模板元編程技術簡化了語法,包裝C++代碼只需要借助一種聲明式接口定義語言declarative interface definition language (IDL)。 

  在Python 中引用 C/C++ 模塊的方法較多,根據需要從中選擇恰當的方法可以減少很多工作量。在Python 中引用 C/C++ 模塊彌補了 Python 腳本測試框架的很多不足,在提高代碼復用率的同時,模塊的性能也大大提高。

仔細閱讀了《Extending and Embedding the python Interpreter》之后,想自己demo一下,做個學習日志,記錄學習python的辛酸之路。

      無論是擴展(Extending)還是嵌入(Embeding),都需要python.h文件。PythonAPI定義了一系列的函數,宏,變量等(在python.h中定義的所有用戶可見的符號都有個前綴py或者PY。)使得調用者可以調用Python運行時系統的大部分功能。這些Python API合成了一個C源文件,你可以通過Python.h訪問這些對象。Python有可能會定義一些有可能影響到標准頭文件的預處理程序,所以應在引用標准頭文件前引用python.h。    

      需要指出的是嵌入和擴展有很多相像的地方(畢竟所謂交互就是提供一種翻譯手段,將C能夠認識的對象轉化為Python所能認識,反之依然),他們主要的區別在於當擴展python時,你編寫的代碼還是在python解釋器運行(你用C定義某個對象最終要轉化成Python對象用python解釋器運行),但嵌入python,跟python解釋器關系就不大了(只是你程序的部分代碼要調用python解釋器去執行某些python代碼完成交互)。相對來說擴展python比嵌入python更加直觀好理解一點。Boost.Python可以簡化你的工作,無論是嵌入,還是擴展。

      要想要自定義模塊被python代碼調用需要把模塊內函數名和地址放入函數表Method Table 中。函數表必須被引用到模塊定義結構module definition structure里。而模塊定義結構必須在模塊的初始化函數中被傳遞給解釋器。初始化函數必須被命名成PyInit_name()。另外在擴展Python時經常會要自定義異常,對於自定義異常的初始化也應該放在初始化函數中完成。

     

 
         

#include "Python.h"

 
         

static PyObject *SpamError; // 自定義異常

// 模塊函數
static PyObject *

spam_system(PyObject *self, PyObject *args)
{
const char *command;
int sts;

 
         

if (!PyArg_ParseTuple(args, "s", &command))
return NULL;
sts = system(command);
if (sts < 0) {
PyErr_SetString(SpamError, "System command failed");
return NULL;
}
return PyLong_FromLong(sts);
}

 
         

// 模塊函數表
static PyMethodDef SpamMethods[] = {
{"system", spam_system, METH_VARARGS,
"Execute a shell command."},
{NULL, NULL}
};

//模塊定義結構體

static struct PyModuleDef spammodule = {
PyModuleDef_HEAD_INIT,
"spam",
"example module doc string",
-1,
SpamMethods,
NULL,
NULL,
NULL,
NULL
};

 
         

PyMODINIT_FUNC
PyInit_system(void)
{
PyObject *m;

 
         

m = PyModule_Create(&spammodule);
if (m == NULL)
return NULL;

 
         

SpamError = PyErr_NewException("spam.error", NULL, NULL);
Py_INCREF(SpamError);
PyModule_AddObject(m, "error", SpamError);
return m;
}

 Note:要使用同一個編譯器去編譯python build 和 這份代碼,否則嵌入后不能夠執行。使用VS2010編譯python3.2源碼中的PC\example_nt下代碼時有需要改動工程文件,將兩行initexample去掉,否則會編譯出錯,至於需要python2.6.lib或者python2.6_d.lib則完全可以使用同一份代碼編譯出3.2版本的lib(注意需要編譯兩個版本,debug版本對應后者,release版本對應前者)代替但同樣需要改動工程文件。

 

      python解釋器有個很重要的約定:當一個函數失敗時,他應該設定異常條件並且返回一個error value(錯誤值)。異常會被存儲在解釋器里的一個靜態的全局變量中,它是一個三元組(type, value, traceback):第一個值代表了異常類型,第二個全局變量會存儲異常的關聯值,第三個變量包含了異常堆棧的追溯對象。這個三元組就是Python中sys.exc_info()的返回值。如果這個三元組值為null,就代表沒有異常發生。

      PyArg_ParseTuple():會檢查參數類型,並把他們轉化成C類型的值。

      python API定義了一組函數用於定義不同類型的異常:

  PyErr_SetString():它的參數是異常對象和一個C類型字符串,異常對象通常是一個預定義對象比如PyExc_ZeroDivisionError,C類型字符串表明了錯誤發生的原因,會被轉換成python字符串對象並且會被當作異常的關聯值存儲起來。

  PyErr_SetFromErrno():只有一個異常參數,用於檢查全局變量errno並構造關聯值。

  PyErr_SetObject():兩個參數,異常和關聯值。 傳遞給這些函數的任何對象都不需要調用Py_INCREF()。

      PyErr_Clear():用於忽略掉函數失敗引發的異常,只有一種情況C代碼會掉用該函數:當不想傳遞異常錯誤信息給解釋器而想自己完全處理異常。

 

Reference:

     http://docs.python.org/py3k/extending/index.html

     http://www.boost.org/doc/libs/1_49_0/libs/python/doc/index.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM