在應用中嵌入Python

2021-06-13 18:55:31 字數 4517 閱讀 6919

翻譯:

gashero

前面的章節討論如何擴充套件python,如何生成適合的c庫等。不過還有另一種情況:通過將python嵌入c/c++應用以擴充套件程式的功能。python嵌入實現了一些使用python更合適的功能。這可以有很多用途,乙個例子是允許使用者裁減需要的python功能。也可以用於默寫使用python編寫更加方便的功能。

嵌入python與擴充套件很像。擴充套件python時,主程式是python直譯器,但是嵌入python則主程式並不是python的-是程式的其他部分呼叫python來實現一些功能。

所以,如果要嵌入python,你可以提供自己的主程式,這個主程式需要初始化python直譯器。至少需要呼叫函式 py_initialize() (對於macos,呼叫 pymac_initialize())。可以選擇是否傳入命令列引數到python。然後你就可以在應用的任何地方呼叫python直譯器了。

有幾種方法呼叫直譯器:可以傳遞乙個包含python語句的字串到 pyrun_******string() ,也可以傳遞乙個stdio檔案指標和乙個檔名(用於識別錯誤資訊)到 pyrun_******file() 。你也可以呼叫前幾章介紹的底層操作直接控制python物件。

可以在目錄 demo/embed/ 中找到嵌入python的例子。目錄

嵌入python最簡單的形式是使用高層次的介面。這個介面專門用於執行python指令碼,而不需要與應用程式直接互動。例子可以在乙個檔案中展示:

#include int

main(int argc, char* argv)

如上**首先使用 py_initialize() 初始化python直譯器,隨後執行硬編碼中的python指令碼來列印日期和時間。最後 py_finalize() 關閉了直譯器。在真實應用中,你可能希望從其他方式獲取python指令碼,檔案、編輯器、資料庫等。從檔案獲取的方式更適合使用 pyrun_******file() 函式,可以省去分配記憶體空間和載入檔案的麻煩。

高層次的介面可以方便的執行python**,但是交換資料就很麻煩。如果需要,你可以使用低層次的介面呼叫。雖然多寫一些c**,但是卻可以完成很多功能。

仍然要提醒的是,python的擴充套件與嵌入其實很像,儘管目的不同。前幾章討論的大多數問題在這裡也同樣適用。可以參考用c擴充套件python時一些步驟:

轉換python型別到c型別

傳遞引數並呼叫c函式

轉換返回值到python

當嵌入python時,介面需要做:

轉換c資料到python

呼叫python介面程式來呼叫python函式

轉化返回值到c

有如你所見,資料轉換的步驟用於跨語言的資料交換。唯一的不同是兩次資料轉換之間呼叫的函式。當擴充套件時,你呼叫c函式,當嵌入時,呼叫python函式。

這一章不會討論python和c之間的資料轉換。並且假設你會使用手冊來處理錯誤,自此只會討論與擴充套件直譯器不同的部分,你可以到前面的章節找到需要的資訊。

第乙個程式是執行一段python指令碼中的函式。有如高層介面一節,python直譯器並不會自動與程式結合。

執行一段python指令碼中的函式的**如下:

#include int

main(int argc, char *argv)

py_initialize();

pname = pystring_fromstring(argv[1]);

/* error checking of pname left out */

pmodule = pyimport_import(pname);

py_decref(pname);

if (pmodule != null)

/* pvalue reference stolen here: */

pytuple_setitem(pargs, i, pvalue);

}pvalue = pyobject_callobject(pfunc, pargs);

py_decref(pargs);

if (pvalue != null)

else

}else

py_xdecref(pfunc);

py_decref(pmodule);

}else

py_finalize();

return 0;

}

這段**從argv[1]中載入python指令碼,並且呼叫argv[2]中的函式,整數型的引數則是從argv陣列後面得來的。如果編譯和鏈結這個程式,執行如下指令碼:

def multiply(a,b):

print "will compute",a,"times",b

c=0for i in range(0,a)

c=c+b

return c

結果將是:

$ call multiply multiply 3 2

will compute 3 times 2

result of call: 6

雖然這個程式的**挺多的,但是大部分其實都是做資料轉換和錯誤報告。主要關於嵌入python的開始於:

py_initialize();

pname=pystring_fromstring(argv[1]);

/* error checking of pname left out */

pmodule=pyimport_import(pname);

初始化直譯器之後,使用 pyimport_import() 匯入模組。這個函式需要字串作為引數,使用 pystring_fromstring() 來構造:

pfunc=pyobject_getattrstring(pmodule,argv[2]);

/* pfunc is a new reference */

if (pfunc && pycallable_check(pfunc))

py_xdecref(pfunc);

載入了模組以後,就可以通過 pyobject_getattrstring() 來獲取物件。如果名字存在並且可以執行則可以安全的呼叫它。程式隨後構造引數元組,然後執行呼叫:

pvalue=pyobject_callobject(pfunc,pargs);
函式呼叫之後,pvalue要麼是null,要麼是返回值的物件引用。注意在檢查完返回值之後要釋放引用。

至今為止,嵌入的python直譯器還不能訪問應用程式本身的功能。python的api允許擴充套件嵌入的python的直譯器。所以,python可以獲得其所嵌入的程式的功能。這聽起來挺麻煩的,其實並不是那樣。只要簡單的忘記是應用程式啟動了python直譯器。

可以把程式看作一對功能的集合,可以寫一些膠水**來來讓python訪問這些功能,有如你在寫乙個普通的python擴充套件一樣。例如:

static int numargs=0;

static pyobject*

emb_numargs(pyobject *self, pyobject *args)

static pymethoddef embmethods = ,

};

新增上面的**到 main() 函式。同樣,插入如下兩個語句到 py_initialize() 函式之後:

numargs=argc;

py_initmodule("emb",embmethods);

這兩行**初始化numargs變數,並且使得 emb.numargs() 函式更加易於被python嵌入的直譯器所理解。通過這個擴充套件,python指令碼可以做如下事情:

import emb

print "number of arguments",emb.numargs()

在實際的應用程式中,方法需要匯出api以供python使用。

有時候需要將python嵌入到c++程式中,而你必須有一些要注意的c++系統的細節,一般來說你要為這個程式寫乙個main()函式,然後使用c++編譯器來編譯和鏈結程式。而這裡不需要因為使用c++而重新編譯python本身。

當 configure 指令碼執行時,可以正確的生成動態鏈結庫使用的匯出符號,而這些卻不會自動被嵌入的靜態鏈結的python所繼承,至少是在unix。這是用於靜態鏈結執行庫(libpython.a)並且需要載入動態擴充套件(.so)的方式。

問題是一些入口點是使用python執行時定義的而僅供擴充套件模組使用。如果嵌入應用不使用任何這些入口點,一些鏈結器不會包含這些實體到最終可執行檔案的符號表。一些附加的選項可以用於告知聯結器不要刪除這些符號。

對於不同的平台,想要正確的檢測該使用何種引數是非常困難的,但是幸運的是python配置好了這些值。只要通過已經安裝的python直譯器,啟動互動直譯器然後執行如下會話即可:

>>> import distutils.sysconfig

>>> distutils.sysconfig.get_config_var('linkforshared')

'-xlinker -export-dynamic'

字串的內容就是生成的選項。如果字串為空,則不需要任何的附加選項。linkforshared的定義與python頂層makefile中的同名變數相同。

c python函式 在C中嵌入python函式

我正在嘗試使用cython從 python生成c 但似乎存在名稱重整的一些問題.我首先生成將 從python轉換為c 然後使用gcc將 編譯成.so.我想使用cython而不是c python api的原因是因為我稍後會在更複雜的類上使用它,我希望以後成為乙個速度等的庫 我很難找到去的人從pytho...

執行緒嵌入技術及在外掛程式中的應用

執行緒嵌入技術及在外掛程式中的應用 關鍵字 程序,執行緒,dll,執行緒嵌入技術 hthread createremotethread hprocess,null,0,pfnthreadrtn,pszlibfileremote,0,null 到這裡執行緒嵌入是大功告成了,接下了寫我們的dll 因為此...

在WinForm應用程式中嵌入WPF控制項

我們知道,在 wpf介面上新增 winform 的控制項需要使用 windowsformhost 類。而在 winform 介面上新增 wpf控制項該如何做呢?有沒有類似的類呢?明顯是有的,elementhost 就是為了在 winform 應用程式中嵌入 wpf元素而定義的。它繼承自system....