如何在C++中调用python程序?
文章目錄
- 1. Embedding Python in Another Application 將Python嵌入另一個應用程序中
- 1.1. Very High Level Embedding 高層嵌入
- 1.2. Beyond Very High Level Embedding: An overview 超越高級嵌入:概述
- 1.3. Pure Embedding 純嵌入
- 1.4. Extending Embedded Python
- 1.5. Embedding Python in C++ 在C ++中嵌入Python
- 1.6. Compiling and Linking under Unix-like systems 編譯和在類Unix系統鏈接
1. Embedding Python in Another Application 將Python嵌入另一個應用程序中
The previous chapters discussed how to extend Python, that is, how to extend the functionality of Python by attaching a library of C functions to it. It is also possible to do it the other way around: enrich your C/C++ application by embedding Python in it. Embedding provides your application with the ability to implement some of the functionality of your application in Python rather than C or C++. This can be used for many purposes; one example would be to allow users to tailor the application to their needs by writing some scripts in Python. You can also use it yourself if some of the functionality can be written in Python more easily.
前面的章節討論了如何擴展Python,即如何通過將C函數庫附加到Python來擴展Python的功能。也可以通過其他方式做到這一點:通過將Python嵌入到C / C ++應用程序中來豐富它。嵌入為您的應用程序提供了使用Python而非C或C ++實現應用程序某些功能的能力。這可以用于許多目的。一個示例是允許用戶通過使用Python編寫一些腳本來根據自己的需求定制應用程序。如果某些功能可以更輕松地用Python編寫,那么您也可以自己使用它。
Embedding Python is similar to extending it, but not quite. The difference is that when you extend Python, the main program of the application is still the Python interpreter, while if you embed Python, the main program may have nothing to do with Python — instead, some parts of the application occasionally call the Python interpreter to run some Python code.
嵌入Python與擴展Python類似,但不完全相同。區別在于,當您擴展Python時,應用程序的主程序仍然是Python解釋器,而如果您嵌入Python,則主程序可能與Python無關—相反,應用程序的某些部分有時會調用Python解釋器運行一些Python代碼。
So if you are embedding Python, you are providing your own main program. One of the things this main program has to do is initialize the Python interpreter. At the very least, you have to call the function Py_Initialize(). There are optional calls to pass command line arguments to Python. Then later you can call the interpreter from any part of the application.
因此,如果您要嵌入Python,那么您將提供自己的主程序。這個主程序要做的一件事就是初始化Python解釋器。至少,您必須調用函數Py_Initialize()。有一些可選的調用,可將命令行參數傳遞給Python。然后,您可以從應用程序的任何部分調用解釋器。
There are several different ways to call the interpreter: you can pass a string containing Python statements to PyRun_SimpleString(), or you can pass a stdio file pointer and a file name (for identification in error messages only) to PyRun_SimpleFile(). You can also call the lower-level operations described in the previous chapters to construct and use Python objects.
有幾種不同的調用解釋器的方式:可以將包含Python語句的字符串傳遞給PyRun_SimpleString(),或者可以將stdio文件指針和文件名(僅用于錯誤消息中的標識)傳遞給PyRun_SimpleFile()。您還可以調用前面各章中描述的較低級別的操作來構造和使用Python對象。
參見
Python/C API 參考手冊
The details of Python’s C interface are given in this manual. A great deal of necessary information can be found here.
1.1. Very High Level Embedding 高層嵌入
The simplest form of embedding Python is the use of the very high level interface. This interface is intended to execute a Python script without needing to interact with the application directly. This can for example be used to perform some operation on a file.
嵌入Python的最簡單形式是使用非常高級的接口。該接口旨在執行Python腳本,而無需直接與應用程序進行交互。例如,這可用于對文件執行某些操作。
#define PY_SSIZE_T_CLEAN #include <Python.h>int main(int argc, char *argv[]) {wchar_t *program = Py_DecodeLocale(argv[0], NULL);if (program == NULL) {fprintf(stderr, "Fatal error: cannot decode argv[0]\n");exit(1);}Py_SetProgramName(program); /* optional but recommended */Py_Initialize();PyRun_SimpleString("from time import time,ctime\n""print('Today is', ctime(time()))\n");if (Py_FinalizeEx() < 0) {exit(120);}PyMem_RawFree(program);return 0; }The Py_SetProgramName() function should be called before Py_Initialize() to inform the interpreter about paths to Python run-time libraries. Next, the Python interpreter is initialized with Py_Initialize(), followed by the execution of a hard-coded Python script that prints the date and time. Afterwards, the Py_FinalizeEx() call shuts the interpreter down, followed by the end of the program. In a real program, you may want to get the Python script from another source, perhaps a text-editor routine, a file, or a database. Getting the Python code from a file can better be done by using the PyRun_SimpleFile() function, which saves you the trouble of allocating memory space and loading the file contents.
Py_SetProgramName()在Py_Initialize()通知解釋器有關Python運行時庫的路徑之前,應先調用該函數 。接下來,使用初始化Python解釋器 Py_Initialize(),然后執行打印日期和時間的硬編碼Python腳本。之后,該Py_FinalizeEx()調用將關閉解釋器,然后結束程序。在實際程序中,您可能希望從其他來源(可能是文本編輯器例程,文件或數據庫)獲取Python腳本。使用該PyRun_SimpleFile()函數可以更好地從文件中獲取Python代碼,從而避免了分配內存空間和加載文件內容的麻煩。
1.2. Beyond Very High Level Embedding: An overview 超越高級嵌入:概述
The high level interface gives you the ability to execute arbitrary pieces of Python code from your application, but exchanging data values is quite cumbersome to say the least. If you want that, you should use lower level calls. At the cost of having to write more C code, you can achieve almost anything.
高級接口使您能夠從應用程序中執行任意段Python代碼,但是至少可以說,交換數據值非常麻煩。如果需要,應該使用較低級別的調用。以編寫更多的C代碼為代價,您幾乎可以實現任何目標。
It should be noted that extending Python and embedding Python is quite the same activity, despite the different intent. Most topics discussed in the previous chapters are still valid. To show this, consider what the extension code from Python to C really does:
應該注意的是,盡管意圖不同,但擴展Python和嵌入Python是完全相同的活動。前幾章討論的大多數主題仍然有效。為了說明這一點,請考慮一下從Python到C的擴展代碼的實際作用:
Convert data values from Python to C,
將數據值從Python轉換為C,
Perform a function call to a C routine using the converted values, and
使用轉換后的值對C例程執行函數調用,然后
Convert the data values from the call from C to Python.
將調用中的數據值從C轉換為Python。
When embedding Python, the interface code does:
嵌入Python時,接口代碼執行以下操作:
Convert data values from C to Python,
將數據值從C轉換為Python,
Perform a function call to a Python interface routine using the converted values, and
使用轉換后的值執行對Python接口例程的函數調用,以及
Convert the data values from the call from Python to C.
將調用中的數據值從Python轉換為C。
As you can see, the data conversion steps are simply swapped to accommodate the different direction of the cross-language transfer. The only difference is the routine that you call between both data conversions. When extending, you call a C routine, when embedding, you call a Python routine.
如您所見,只需轉換數據轉換步驟即可適應跨語言傳輸的不同方向。唯一的區別是兩次數據轉換之間調用的例程。擴展時,您調用C例程,嵌入時,您調用Python例程。
This chapter will not discuss how to convert data from Python to C and vice versa. Also, proper use of references and dealing with errors is assumed to be understood. Since these aspects do not differ from extending the interpreter, you can refer to earlier chapters for the required information.
1.3. Pure Embedding 純嵌入
The first program aims to execute a function in a Python script. Like in the section about the very high level interface, the Python interpreter does not directly interact with the application (but that will change in the next section).
第一個程序旨在在Python腳本中執行功能。就像在有關高級界面的部分中一樣,Python解釋器不會直接與應用程序進行交互(但是在下一部分中會有所變化)。
The code to run a function defined in a Python script is:
運行Python腳本中定義的函數的代碼為:
#define PY_SSIZE_T_CLEAN #include <Python.h>int main(int argc, char *argv[]) {PyObject *pName, *pModule, *pFunc;PyObject *pArgs, *pValue;int i;if (argc < 3) {fprintf(stderr,"Usage: call pythonfile funcname [args]\n");return 1;}Py_Initialize();pName = PyUnicode_DecodeFSDefault(argv[1]);/* Error checking of pName left out */pModule = PyImport_Import(pName);Py_DECREF(pName);if (pModule != NULL) {pFunc = PyObject_GetAttrString(pModule, argv[2]);/* pFunc is a new reference */if (pFunc && PyCallable_Check(pFunc)) {pArgs = PyTuple_New(argc - 3);for (i = 0; i < argc - 3; ++i) {pValue = PyLong_FromLong(atoi(argv[i + 3]));if (!pValue) {Py_DECREF(pArgs);Py_DECREF(pModule);fprintf(stderr, "Cannot convert argument\n");return 1;}/* pValue reference stolen here: */PyTuple_SetItem(pArgs, i, pValue);}pValue = PyObject_CallObject(pFunc, pArgs);Py_DECREF(pArgs);if (pValue != NULL) {printf("Result of call: %ld\n", PyLong_AsLong(pValue));Py_DECREF(pValue);}else {Py_DECREF(pFunc);Py_DECREF(pModule);PyErr_Print();fprintf(stderr,"Call failed\n");return 1;}}else {if (PyErr_Occurred())PyErr_Print();fprintf(stderr, "Cannot find function \"%s\"\n", argv[2]);}Py_XDECREF(pFunc);Py_DECREF(pModule);}else {PyErr_Print();fprintf(stderr, "Failed to load \"%s\"\n", argv[1]);return 1;}if (Py_FinalizeEx() < 0) {return 120;}return 0; }This code loads a Python script using argv[1], and calls the function named in argv[2]. Its integer arguments are the other values of the argv array. If you compile and link this program (let’s call the finished executable call), and use it to execute a Python script, such as:
此代碼使用加載Python腳本argv[1],并調用中命名的函數argv[2]。它的整數參數是argv 數組的其他值。如果編譯并鏈接該程序(讓我們調用完成的可執行調用),并使用它執行Python腳本,例如:
def multiply(a,b):print("Will compute", a, "times", b)c = 0for i in range(0, a):c = c + breturn cthen the result should be:
那么結果應該是:
$ call multiply multiply 3 2 Will compute 3 times 2 Result of call: 6Although the program is quite large for its functionality, most of the code is for data conversion between Python and C, and for error reporting. The interesting part with respect to embedding Python starts with
盡管該程序的功能非常龐大,但大多數代碼都是用于Python和C之間的數據轉換以及錯誤報告。關于嵌入Python的有趣部分始于
Py_Initialize(); pName = PyUnicode_DecodeFSDefault(argv[1]); /* Error checking of pName left out */ pModule = PyImport_Import(pName);After initializing the interpreter, the script is loaded using PyImport_Import(). This routine needs a Python string as its argument, which is constructed using the PyUnicode_FromString() data conversion routine.
初始化解釋器后,使用加載腳本 PyImport_Import()。該例程需要一個Python字符串作為其參數,該字符串是使用PyUnicode_FromString()數據轉換例程構造的。
pFunc = PyObject_GetAttrString(pModule, argv[2]); /* pFunc is a new reference */if (pFunc && PyCallable_Check(pFunc)) {... } Py_XDECREF(pFunc);Once the script is loaded, the name we’re looking for is retrieved using PyObject_GetAttrString(). If the name exists, and the object returned is callable, you can safely assume that it is a function. The program then proceeds by constructing a tuple of arguments as normal. The call to the Python function is then made with:
加載腳本后,將使用查找所需的名稱 PyObject_GetAttrString()。如果名稱存在,并且返回的對象是可調用的,則可以安全地假定它是一個函數。然后,程序通過正常構造參數元組繼續進行。然后使用以下命令調用Python函數:
pValue = PyObject_CallObject(pFunc, pArgs);Upon return of the function, pValue is either NULL or it contains a reference to the return value of the function. Be sure to release the reference after examining the value.
函數返回時,pValue是NULL或包含對函數返回值的引用。檢查值之后,請確保釋放參考。
1.4. Extending Embedded Python
Until now, the embedded Python interpreter had no access to functionality from the application itself. The Python API allows this by extending the embedded interpreter. That is, the embedded interpreter gets extended with routines provided by the application. While it sounds complex, it is not so bad. Simply forget for a while that the application starts the Python interpreter. Instead, consider the application to be a set of subroutines, and write some glue code that gives Python access to those routines, just like you would write a normal Python extension. For example:
到目前為止,嵌入式Python解釋器無法從應用程序本身訪問功能。Python API通過擴展嵌入式解釋器來實現這一點。也就是說,嵌入式解釋器將使用應用程序提供的例程進行擴展。雖然聽起來很復雜,但還不錯。只需暫時忘記應用程序啟動Python解釋器。相反,應將應用程序視為一組子例程,并編寫一些粘合代碼以使Python可以訪問這些例程,就像編寫普通的Python擴展一樣。例如:
static int numargs=0;/* Return the number of arguments of the application command line */ static PyObject* emb_numargs(PyObject *self, PyObject *args) {if(!PyArg_ParseTuple(args, ":numargs"))return NULL;return PyLong_FromLong(numargs); }static PyMethodDef EmbMethods[] = {{"numargs", emb_numargs, METH_VARARGS,"Return the number of arguments received by the process."},{NULL, NULL, 0, NULL} };static PyModuleDef EmbModule = {PyModuleDef_HEAD_INIT, "emb", NULL, -1, EmbMethods,NULL, NULL, NULL, NULL };static PyObject* PyInit_emb(void) {return PyModule_Create(&EmbModule); }Insert the above code just above the main() function. Also, insert the following two statements before the call to Py_Initialize():
在main()函數上方插入上面的代碼。另外,在對的調用之前插入以下兩個語句Py_Initialize():
numargs = argc; PyImport_AppendInittab("emb", &PyInit_emb);These two lines initialize the numargs variable, and make the emb.numargs() function accessible to the embedded Python interpreter. With these extensions, the Python script can do things like
這兩行初始化numargs變量,并使該 emb.numargs()函數可被嵌入式Python解釋器訪問。通過這些擴展,Python腳本可以執行以下操作
import emb print("Number of arguments", emb.numargs())In a real application, the methods will expose an API of the application to Python.
在實際的應用程序中,這些方法會將應用程序的API公開給Python。
1.5. Embedding Python in C++ 在C ++中嵌入Python
It is also possible to embed Python in a C++ program; precisely how this is done will depend on the details of the C++ system used; in general you will need to write the main program in C++, and use the C++ compiler to compile and link your program. There is no need to recompile Python itself using C++.
也可以將Python嵌入C ++程序中。確切地講,這將取決于所使用的C ++系統的細節。通常,您將需要用C ++編寫主程序,并使用C ++編譯器來編譯和鏈接程序。無需使用C ++重新編譯Python本身。
1.6. Compiling and Linking under Unix-like systems 編譯和在類Unix系統鏈接
It is not necessarily trivial to find the right flags to pass to your compiler (and linker) in order to embed the Python interpreter into your application, particularly because Python needs to load library modules implemented as C dynamic extensions (.so files) linked against it.
找到正確的標志傳遞給編譯器(和鏈接器)以將Python解釋器嵌入到您的應用程序中并不一定很簡單,尤其是因為Python需要加載實現為.so與其鏈接的C動態擴展(文件)的庫模塊。
To find out the required compiler and linker flags, you can execute the pythonX.Y-config script which is generated as part of the installation process (a python3-config script may also be available). This script has several options, of which the following will be directly useful to you:
要找出所需的編譯器和鏈接器標志,您可以執行 在安裝過程中生成的腳本(也可以使用腳本)。該腳本有多個選項,其中的以下選項對您直接有用:pythonX.Y-configpython3-config
- pythonX.Y-config --cflags will give you the recommended flags when compiling:
pythonX.Y-config --cflags 編譯時會給您推薦的標志:
- pythonX.Y-config --ldflags will give you the recommended flags when linking:
pythonX.Y-config --ldflags 鏈接時會為您提供推薦的標志:
注解 To avoid confusion between several Python installations (and especially between the system Python and your own compiled Python), it is recommended that you use the absolute path to pythonX.Y-config, as in the above example.
注解 為了避免一些Python的設備之間的混淆(尤其是系統Python和自己編譯的Python之間),因此建議您使用絕對路徑,如上面的例子。pythonX.Y-config
If this procedure doesn’t work for you (it is not guaranteed to work for all Unix-like platforms; however, we welcome bug reports) you will have to read your system’s documentation about dynamic linking and/or examine Python’s Makefile (use sysconfig.get_makefile_filename() to find its location) and compilation options. In this case, the sysconfig module is a useful tool to programmatically extract the configuration values that you will want to combine together. For example:
如果此程序對您不起作用(不能保證在所有類Unix平臺上都有效;但是,我們歡迎提供錯誤報告),則您必須閱讀系統文檔中有關動態鏈接和/或檢查Python的文檔Makefile(用于sysconfig.get_makefile_filename() 查找其位置)和編譯選項。在這種情況下,該sysconfig模塊是有用的工具,可用于以編程方式提取要組合在一起的配置值。例如:
>>> import sysconfig >>> sysconfig.get_config_var('LIBS') '-lpthread -ldl -lutil' >>> sysconfig.get_config_var('LINKFORSHARED') '-Xlinker -export-dynamic'參考文章:Embedding Python in Another Application
總結
以上是生活随笔為你收集整理的如何在C++中调用python程序?的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python opencv cv2.re
- 下一篇: python numpy np.full