日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
創(chuàng)新互聯(lián)Python教程:1.在其它應(yīng)用程序嵌入Python

1. 在其它應(yīng)用程序嵌入 python

前幾章討論了如何對 Python 進(jìn)行擴(kuò)展,也就是如何用 C 函數(shù)庫 擴(kuò)展 Python 的功能。反過來也是可以的:將 Python 嵌入到 C/C++ 應(yīng)用程序中豐富其功能。這種嵌入可以讓應(yīng)用程序用 Python 來實(shí)現(xiàn)某些功能,而不是用 C 或 C++ 。用途會(huì)有很多;比如允許用戶用 Python 編寫一些腳本,以便定制應(yīng)用程序滿足需求。如果某些功能用 Python 編寫起來更為容易,那么開發(fā)人員自己也能這么干。

成都創(chuàng)新互聯(lián)是專業(yè)的瀾滄網(wǎng)站建設(shè)公司,瀾滄接單;提供成都網(wǎng)站設(shè)計(jì)、做網(wǎng)站,網(wǎng)頁設(shè)計(jì),網(wǎng)站設(shè)計(jì),建網(wǎng)站,PHP網(wǎng)站建設(shè)等專業(yè)做網(wǎng)站服務(wù);采用PHP框架,可快速的進(jìn)行瀾滄網(wǎng)站開發(fā)網(wǎng)頁制作和功能擴(kuò)展;專業(yè)做搜索引擎喜愛的網(wǎng)站,專業(yè)的做網(wǎng)站團(tuán)隊(duì),希望更多企業(yè)前來合作!

Python 的嵌入類似于擴(kuò)展,但不完全相同。不同之處在于,擴(kuò)展 Python 時(shí)應(yīng)用程序的主程序仍然是 Python 解釋器,而嵌入 Python 時(shí)的主程序可能與 Python 完全無關(guān)——而是應(yīng)用程序的某些部分偶爾會(huì)調(diào)用 Python 解釋器來運(yùn)行一些 Python 代碼。

因此,若要嵌入 Python,就要提供自己的主程序。此主程序要做的事情之一就是初始化 Python 解釋器。至少得調(diào)用函數(shù) Py_Initialize()。還有些可選的調(diào)用可向 Python 傳遞命令行參數(shù)。之后即可從應(yīng)用程序的任何地方調(diào)用解釋器了。

調(diào)用解釋器的方式有好幾種:可向 PyRun_SimpleString() 傳入一個(gè)包含 Python 語句的字符串,也可向 PyRun_SimpleFile() 傳入一個(gè) stdio 文件指針和一個(gè)文件名(僅在錯(cuò)誤信息中起到識別作用)。還可以調(diào)用前面介紹過的底層操作來構(gòu)造并使用 Python 對象。

參見

Python/C API 參考手冊

本文詳細(xì)介紹了 Python 的 C 接口。這里有大量必要的信息。

1.1. 高層次的嵌入

最簡單的 Python 嵌入形式就是采用非常高層的接口。該接口的目標(biāo)是只執(zhí)行一段 Python 腳本,而無需與應(yīng)用程序直接交互。比如以下代碼可以用來對某個(gè)文件進(jìn)行一些操作。

 
 
 
 
  1. #define PY_SSIZE_T_CLEAN
  2. #include
  3. int
  4. main(int argc, char *argv[])
  5. {
  6. wchar_t *program = Py_DecodeLocale(argv[0], NULL);
  7. if (program == NULL) {
  8. fprintf(stderr, "Fatal error: cannot decode argv[0]\n");
  9. exit(1);
  10. }
  11. Py_SetProgramName(program); /* optional but recommended */
  12. Py_Initialize();
  13. PyRun_SimpleString("from time import time,ctime\n"
  14. "print('Today is', ctime(time()))\n");
  15. if (Py_FinalizeEx() < 0) {
  16. exit(120);
  17. }
  18. PyMem_RawFree(program);
  19. return 0;
  20. }

在 Py_Initialize() 之前,應(yīng)該先調(diào)用 Py_SetProgramName() 函數(shù),以便向解釋器告知 Python運(yùn)行庫的路徑。接下來,Py_Initialize() 會(huì)初始化 Python 解釋器,然后執(zhí)行硬編碼的 Python 腳本,打印出日期和時(shí)間。之后,調(diào)用 Py_FinalizeEx() 關(guān)閉解釋器,程序結(jié)束。在真實(shí)的程序中,可能需要從其他來源獲取 Python 腳本,或許是從文本編輯器例程、文件,或者某個(gè)數(shù)據(jù)庫。利用 PyRun_SimpleFile() 函數(shù)可以更好地從文件中獲取 Python 代碼,可省去分配內(nèi)存空間和加載文件內(nèi)容的麻煩。

1.2. 突破高層次嵌入的限制:概述

高級接口能從應(yīng)用程序中執(zhí)行任何 Python 代碼,但至少交換數(shù)據(jù)可說是相當(dāng)麻煩的。如若需要交換數(shù)據(jù),應(yīng)使用較低級別的調(diào)用。幾乎可以實(shí)現(xiàn)任何功能,代價(jià)是得寫更多的 C 代碼。

應(yīng)該注意,盡管意圖不同,但擴(kuò)展 Python 和嵌入 Python 的過程相當(dāng)類似。前幾章中討論的大多數(shù)主題依然有效。為了說明這一點(diǎn),不妨來看一下從 Python 到 C 的擴(kuò)展代碼到底做了什么:

  1. 將 Python 的數(shù)據(jù)轉(zhuǎn)換為 C 格式,

  2. 用轉(zhuǎn)換后的數(shù)據(jù)執(zhí)行 C 程序的函數(shù)調(diào)用,

  3. 將調(diào)用返回的數(shù)據(jù)從 C 轉(zhuǎn)換為 Python 格式。

嵌入 Python 時(shí),接口代碼會(huì)這樣做:

  1. 將 C 數(shù)據(jù)轉(zhuǎn)換為 Python 格式,

  2. 用轉(zhuǎn)換后的數(shù)據(jù)執(zhí)行對 Python 接口的函數(shù)調(diào)用,

  3. 將調(diào)用返回的數(shù)據(jù)從 Python 轉(zhuǎn)換為 C 格式。

可見只是數(shù)據(jù)轉(zhuǎn)換的步驟交換了一下順序,以順應(yīng)跨語言的傳輸方向。唯一的區(qū)別是在兩次數(shù)據(jù)轉(zhuǎn)換之間調(diào)用的函數(shù)不同。在執(zhí)行擴(kuò)展時(shí),調(diào)用一個(gè) C 函數(shù),而執(zhí)行嵌入時(shí)調(diào)用的是個(gè) Python 函數(shù)。

本文不會(huì)討論如何將數(shù)據(jù)從 Python 轉(zhuǎn)換到 C 去,反之亦然。另外還假定讀者能夠正確使用引用并處理錯(cuò)誤。由于這些地方與解釋器的擴(kuò)展沒有區(qū)別,請參考前面的章節(jié)以獲得所需的信息。

1.3. 只做嵌入

第一個(gè)程序的目標(biāo)是執(zhí)行 Python 腳本中的某個(gè)函數(shù)。就像高層次接口那樣,Python 解釋器并不會(huì)直接與應(yīng)用程序進(jìn)行交互(但下一節(jié)將改變這一點(diǎn))。

要運(yùn)行 Python 腳本中定義的函數(shù),代碼如下:

 
 
 
 
  1. #define PY_SSIZE_T_CLEAN
  2. #include
  3. int
  4. main(int argc, char *argv[])
  5. {
  6. PyObject *pName, *pModule, *pFunc;
  7. PyObject *pArgs, *pValue;
  8. int i;
  9. if (argc < 3) {
  10. fprintf(stderr,"Usage: call pythonfile funcname [args]\n");
  11. return 1;
  12. }
  13. Py_Initialize();
  14. pName = PyUnicode_DecodeFSDefault(argv[1]);
  15. /* Error checking of pName left out */
  16. pModule = PyImport_Import(pName);
  17. Py_DECREF(pName);
  18. if (pModule != NULL) {
  19. pFunc = PyObject_GetAttrString(pModule, argv[2]);
  20. /* pFunc is a new reference */
  21. if (pFunc && PyCallable_Check(pFunc)) {
  22. pArgs = PyTuple_New(argc - 3);
  23. for (i = 0; i < argc - 3; ++i) {
  24. pValue = PyLong_FromLong(atoi(argv[i + 3]));
  25. if (!pValue) {
  26. Py_DECREF(pArgs);
  27. Py_DECREF(pModule);
  28. fprintf(stderr, "Cannot convert argument\n");
  29. return 1;
  30. }
  31. /* pValue reference stolen here: */
  32. PyTuple_SetItem(pArgs, i, pValue);
  33. }
  34. pValue = PyObject_CallObject(pFunc, pArgs);
  35. Py_DECREF(pArgs);
  36. if (pValue != NULL) {
  37. printf("Result of call: %ld\n", PyLong_AsLong(pValue));
  38. Py_DECREF(pValue);
  39. }
  40. else {
  41. Py_DECREF(pFunc);
  42. Py_DECREF(pModule);
  43. PyErr_Print();
  44. fprintf(stderr,"Call failed\n");
  45. return 1;
  46. }
  47. }
  48. else {
  49. if (PyErr_Occurred())
  50. PyErr_Print();
  51. fprintf(stderr, "Cannot find function \"%s\"\n", argv[2]);
  52. }
  53. Py_XDECREF(pFunc);
  54. Py_DECREF(pModule);
  55. }
  56. else {
  57. PyErr_Print();
  58. fprintf(stderr, "Failed to load \"%s\"\n", argv[1]);
  59. return 1;
  60. }
  61. if (Py_FinalizeEx() < 0) {
  62. return 120;
  63. }
  64. return 0;
  65. }

上述代碼先利用 argv[1] 加載 Python 腳本,再調(diào)用 argv[2] 指定的函數(shù)。函數(shù)的整數(shù)參數(shù)是 argv 數(shù)組中的其余值。如果 編譯并鏈接 該程序(此處將最終的可執(zhí)行程序稱作 call), 并用它執(zhí)行一個(gè) Python 腳本,例如:

 
 
 
 
  1. def multiply(a,b):
  2. print("Will compute", a, "times", b)
  3. c = 0
  4. for i in range(0, a):
  5. c = c + b
  6. return c

然后結(jié)果應(yīng)該是:

 
 
 
 
  1. $ call multiply multiply 3 2
  2. Will compute 3 times 2
  3. Result of call: 6

盡管相對其功能而言,該程序體積相當(dāng)龐大,但大部分代碼是用于 Python 和 C 之間的數(shù)據(jù)轉(zhuǎn)換,以及報(bào)告錯(cuò)誤。嵌入 Python 的有趣部分從此開始:

 
 
 
 
  1. Py_Initialize();
  2. pName = PyUnicode_DecodeFSDefault(argv[1]);
  3. /* Error checking of pName left out */
  4. pModule = PyImport_Import(pName);

初始化解釋器之后,則用 PyImport_Import() 加載腳本。此函數(shù)的參數(shù)需是個(gè) Python 字符串,一個(gè)用 PyUnicode_FromString() 數(shù)據(jù)轉(zhuǎn)換函數(shù)構(gòu)建的字符串。

 
 
 
 
  1. pFunc = PyObject_GetAttrString(pModule, argv[2]);
  2. /* pFunc is a new reference */
  3. if (pFunc && PyCallable_Check(pFunc)) {
  4. ...
  5. }
  6. Py_XDECREF(pFunc);

腳本一旦加載完畢,就會(huì)用 PyObject_GetAttrString() 查找屬性名稱。如果名稱存在,并且返回的是可調(diào)用對象,即可安全地視其為函數(shù)。然后程序繼續(xù)執(zhí)行,照常構(gòu)建由參數(shù)組成的元組。然后用以下方式調(diào)用 Python 函數(shù):

 
 
 
 
  1. pValue = PyObject_CallObject(pFunc, pArgs);

當(dāng)函數(shù)返回時(shí),pValue 要么為 NULL,要么包含對函數(shù)返回值的引用。請確保用完后釋放該引用。

1.4. 對嵌入 Python 功能進(jìn)行擴(kuò)展

到目前為止,嵌入的 Python 解釋器還不能訪問應(yīng)用程序本身的功能。Python API 通過擴(kuò)展嵌入解釋器實(shí)現(xiàn)了這一點(diǎn)。 也就是說,用應(yīng)用程序提供的函數(shù)對嵌入的解釋器進(jìn)行擴(kuò)展。雖然聽起來有些復(fù)雜,但也沒那么糟糕。只要暫時(shí)忘記是應(yīng)用程序啟動(dòng)了 Python 解釋器。而把應(yīng)用程序看作是一堆子程序,然后寫一些膠水代碼讓 Python 訪問這些子程序,就像編寫普通的 Python 擴(kuò)展程序一樣。 例如:

 
 
 
 
  1. static int numargs=0;
  2. /* Return the number of arguments of the application command line */
  3. static PyObject*
  4. emb_numargs(PyObject *self, PyObject *args)
  5. {
  6. if(!PyArg_ParseTuple(args, ":numargs"))
  7. return NULL;
  8. return PyLong_FromLong(numargs);
  9. }
  10. static PyMethodDef EmbMethods[] = {
  11. {"numargs", emb_numargs, METH_VARARGS,
  12. "Return the number of arguments received by the process."},
  13. {NULL, NULL, 0, NULL}
  14. };
  15. static PyModuleDef EmbModule = {
  16. PyModuleDef_HEAD_INIT, "emb", NULL, -1, EmbMethods,
  17. NULL, NULL, NULL, NULL
  18. };
  19. static PyObject*
  20. PyInit_emb(void)
  21. {
  22. return PyModule_Create(&EmbModule);
  23. }

main() 函數(shù)之前插入上述代碼。并在調(diào)用 Py_Initialize() 之前插入以下兩條語句:

 
 
 
 
  1. numargs = argc;
  2. PyImport_AppendInittab("emb", &PyInit_emb);

這兩行代碼初始化了 numargs 變量,并讓 emb.numargs() 函數(shù)能被嵌入的 Python 解釋器訪問到。有了這些擴(kuò)展,Python 腳本可以執(zhí)行類似以下功能:

 
 
 
 
  1. import emb
  2. print("Number of arguments", emb.numargs())

在真實(shí)的應(yīng)用程序中,這種方法將把應(yīng)用的 API 暴露給 Python 使用。

1.5. 在 C++ 中嵌入 Python

還可以將 Python 嵌入到 C++ 程序中去;確切地說,實(shí)現(xiàn)方式將取決于 C++ 系統(tǒng)的實(shí)現(xiàn)細(xì)節(jié);一般需用 C++ 編寫主程序,并用 C++ 編譯器來編譯和鏈接程序。不需要用 C++ 重新編譯 Python 本身。

1.6. 在類 Unix 系統(tǒng)中編譯和鏈接

為了將 Python 解釋器嵌入應(yīng)用程序,找到正確的編譯參數(shù)傳給編譯器 (和鏈接器) 并非易事,特別是因?yàn)?Python 加載的庫模塊是以 C 動(dòng)態(tài)擴(kuò)展(.so 文件)的形式實(shí)現(xiàn)的。

為了得到所需的編譯器和鏈接器參數(shù),可執(zhí)行 python*X.Y*-config 腳本,它是在安裝 Python 時(shí)生成的(也可能存在 Python3-config 腳本)。該腳本有幾個(gè)參數(shù),其中以下幾個(gè)參數(shù)會(huì)直接有用:

  • pythonX.Y-config --cflags 將給出建議的編譯參數(shù)。

       
       
       
       
    1. $ /opt/bin/python3.11-config --cflags
    2. -I/opt/include/python3.11 -I/opt/include/python3.11 -Wsign-compare -DNDEBUG -g -fwrapv -O3 -Wall
  • pythonX.Y-config --ldflags --embed will give you the recommended flags when linking:

       
       
       
       
    1. $ /opt/bin/python3.11-config --ldflags --embed
    2. -L/opt/lib/python3.11/config-3.11-x86_64-linux-gnu -L/opt/lib -lpython3.11 -lpthread -ldl -lutil -lm

備注

為了避免多個(gè) Python 安裝版本引發(fā)混亂(特別是在系統(tǒng)安裝版本和自己編譯版本之間),建議用 python*X.Y*-config 指定絕對路徑,如上例所述。

如果上述方案不起作用(不能保證對所有 Unix 類平臺(tái)都生效;歡迎提出 bug 報(bào)告),就得閱讀系統(tǒng)關(guān)于動(dòng)態(tài)鏈接的文檔,并檢查 Python 的 Makefile (用 sysconfig.get_makefile_filename() 找到所在位置)和編譯參數(shù)。這時(shí) sysconfig 模塊會(huì)是個(gè)有用的工具,可用編程方式提取需組合在一起的配置值。比如:

 
 
 
 
  1. >>> import sysconfig
  2. >>> sysconfig.get_config_var('LIBS')
  3. '-lpthread -ldl -lutil'
  4. >>> sysconfig.get_config_var('LINKFORSHARED')
  5. '-Xlinker -export-dynamic'

網(wǎng)頁題目:創(chuàng)新互聯(lián)Python教程:1.在其它應(yīng)用程序嵌入Python
文章地址:http://www.5511xx.com/article/cosishs.html