“Python/docs/3.9/extending/embedding”的版本间差异

来自菜鸟教程
Python/docs/3.9/extending/embedding
跳转至:导航、​搜索
(autoload)
 
(Page commit)
 
第1行: 第1行:
 +
{{DISPLAYTITLE:1. 在另一个应用程序中嵌入 Python — Python 文档}}
 
<div id="embedding-python-in-another-application" class="section">
 
<div id="embedding-python-in-another-application" class="section">
  
 
<span id="embedding"></span>
 
<span id="embedding"></span>
= <span class="section-number">1. </span>Embedding Python in Another Application =
+
= 1. 在另一个应用程序中嵌入 Python =
  
The previous chapters discussed how to extend Python, that is, how to extend the
+
前面的章节讨论了如何扩展 Python,即如何通过附加 C 函数库来扩展 Python 的功能。 也可以反过来做:通过在其中嵌入 Python 来丰富您的 C/C++ 应用程序。 嵌入使您的应用程序能够使用 Python 而不是 C C++ 来实现应用程序的某些功能。 这可以用于许多目的; 一个例子是允许用户通过用 Python 编写一些脚本来根据他们的需要定制应用程序。 如果某些功能可以更轻松地用 Python 编写,您也可以自己使用它。
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.
 
  
Embedding Python is similar to extending it, but not quite. The difference is
+
嵌入 Python 类似于扩展它,但不完全相同。 不同的是,当你扩展 Python 时,应用程序的主程序仍然是 Python 解释器,而如果你嵌入 Python,主程序可能与 Python 无关——相反,应用程序的某些部分偶尔会调用 Python 解释器运行一些 Python 代码。
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.
 
  
So if you are embedding Python, you are providing your own main program. One of
+
因此,如果您要嵌入 Python,您将提供自己的主程序。 这个主程序必须做的一件事是初始化 Python 解释器。 至少,您必须调用函数 [[../../c-api/init#c|Py_Initialize()]]。 有一些可选调用可以将命令行参数传递给 Python。 然后,您可以从应用程序的任何部分调用解释器。
the things this main program has to do is initialize the Python interpreter. At
 
the very least, you have to call the function [[../../c-api/init#c|<code>Py_Initialize()</code>]]. There are
 
optional calls to pass command line arguments to Python. Then later you can
 
call the interpreter from any part of the application.
 
  
There are several different ways to call the interpreter: you can pass a string
+
有几种不同的调用解释器的方法:您可以将包含 Python 语句的字符串传递给 [[../../c-api/veryhigh#c|PyRun_SimpleString()]],或者您可以将 stdio 文件指针和文件名(仅用于在错误消息中识别)传递给[[../../c-api/veryhigh#c|PyRun_SimpleFile()]]。 您还可以调用前面章节中描述的低级操作来构造和使用 Python 对象。
containing Python statements to [[../../c-api/veryhigh#c|<code>PyRun_SimpleString()</code>]], or you can pass a
 
stdio file pointer and a file name (for identification in error messages only)
 
to [[../../c-api/veryhigh#c|<code>PyRun_SimpleFile()</code>]]. You can also call the lower-level operations
 
described in the previous chapters to construct and use Python objects.
 
  
 
<div class="admonition seealso">
 
<div class="admonition seealso">
  
参见
+
也可以看看
  
; [[../../c-api/index#c-api-index|<span class="std std-ref">Python/C API Reference Manual</span>]]
+
; [[../../c-api/index#c-api-index|<span class="std std-ref">Python/C API 参考手册</span>]]
: The details of Python's C interface are given in this manual. A great deal of necessary information can be found here.
+
: 本手册中给出了 Python C 接口的详细信息。 在这里可以找到大量必要的信息。
  
  
第44行: 第25行:
  
 
<span id="high-level-embedding"></span>
 
<span id="high-level-embedding"></span>
== <span class="section-number">1.1. </span>Very High Level Embedding ==
+
== 1.1. 非常高级的嵌入 ==
  
The simplest form of embedding Python is the use of the very high level
+
嵌入 Python 的最简单形式是使用非常高级的接口。 此接口旨在执行 Python 脚本,而无需直接与应用程序交互。 例如,这可用于对文件执行某些操作。
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.
 
  
 
<div class="highlight-c notranslate">
 
<div class="highlight-c notranslate">
第55行: 第33行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>#define PY_SSIZE_T_CLEAN
+
<syntaxhighlight lang="c">#define PY_SSIZE_T_CLEAN
#include &lt;Python.h&gt;
+
#include <Python.h>
  
 
int
 
int
第63行: 第41行:
 
     wchar_t *program = Py_DecodeLocale(argv[0], NULL);
 
     wchar_t *program = Py_DecodeLocale(argv[0], NULL);
 
     if (program == NULL) {
 
     if (program == NULL) {
         fprintf(stderr, &quot;Fatal error: cannot decode argv[0]\n&quot;);
+
         fprintf(stderr, "Fatal error: cannot decode argv[0]\n");
 
         exit(1);
 
         exit(1);
 
     }
 
     }
 
     Py_SetProgramName(program);  /* optional but recommended */
 
     Py_SetProgramName(program);  /* optional but recommended */
 
     Py_Initialize();
 
     Py_Initialize();
     PyRun_SimpleString(&quot;from time import time,ctime\n&quot;
+
     PyRun_SimpleString("from time import time,ctime\n"
                       &quot;print('Today is', ctime(time()))\n&quot;);
+
                       "print('Today is', ctime(time()))\n");
     if (Py_FinalizeEx() &lt; 0) {
+
     if (Py_FinalizeEx() < 0) {
 
         exit(120);
 
         exit(120);
 
     }
 
     }
 
     PyMem_RawFree(program);
 
     PyMem_RawFree(program);
 
     return 0;
 
     return 0;
}</pre>
+
}</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
The [[../../c-api/init#c|<code>Py_SetProgramName()</code>]] function should be called before
+
[[../../c-api/init#c|Py_SetProgramName()]] 函数应该在 [[../../c-api/init#c|Py_Initialize()]] 之前调用,以通知解释器有关 Python 运行时库的路径。 接下来,使用 [[../../c-api/init#c|Py_Initialize()]] 初始化 Python 解释器,然后执行打印日期和时间的硬编码 Python 脚本。 之后, [[../../c-api/init#c|Py_FinalizeEx()]] 调用关闭解释器,然后程序结束。 在实际程序中,您可能希望从其他来源(可能是文本编辑器例程、文件或数据库)获取 Python 脚本。 使用 [[../../c-api/veryhigh#c|PyRun_SimpleFile()]] 函数可以更好地从文件中获取 Python 代码,这样可以省去分配内存空间和加载文件内容的麻烦。
[[../../c-api/init#c|<code>Py_Initialize()</code>]] to inform the interpreter about paths to Python run-time
 
libraries. Next, the Python interpreter is initialized with
 
[[../../c-api/init#c|<code>Py_Initialize()</code>]], followed by the execution of a hard-coded Python script
 
that prints the date and time. Afterwards, the [[../../c-api/init#c|<code>Py_FinalizeEx()</code>]] 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 [[../../c-api/veryhigh#c|<code>PyRun_SimpleFile()</code>]] function, which saves you the
 
trouble of allocating memory space and loading the file contents.
 
  
  
第96行: 第65行:
  
 
<span id="lower-level-embedding"></span>
 
<span id="lower-level-embedding"></span>
== <span class="section-number">1.2. </span>Beyond Very High Level Embedding: An overview ==
+
== 1.2. 超越非常高级的嵌入:概述 ==
  
The high level interface gives you the ability to execute arbitrary pieces of
+
高级接口使您能够从应用程序执行任意 Python 代码段,但至少可以说交换数据值非常麻烦。 如果需要,您应该使用较低级别的调用。 以必须编写更多 C 代码为代价,您几乎可以实现任何目标。
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.
 
  
It should be noted that extending Python and embedding Python is quite the same
+
应该注意的是,尽管意图不同,但扩展 Python 和嵌入 Python 是完全相同的活动。 前几章中讨论的大多数主题仍然有效。 为了说明这一点,请考虑从 Python C 的扩展代码的真正作用:
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:
 
  
# 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
+
如您所见,数据转换步骤只是交换以适应跨语言传输的不同方向。 唯一的区别是您在两次数据转换之间调用的例程。 扩展时调用 C 例程,嵌入时调用 Python 例程。
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.
 
  
This chapter will not discuss how to convert data from Python to C and vice
+
本章不会讨论如何将数据从 Python 转换为 C,反之亦然。 此外,假定已理解正确使用引用和处理错误。 由于这些方面与扩展解释器没有区别,因此您可以参考前面的章节以获取所需信息。
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.
 
  
  
第133行: 第90行:
  
 
<span id="id1"></span>
 
<span id="id1"></span>
== <span class="section-number">1.3. </span>Pure Embedding ==
+
== 1.3. 纯嵌入 ==
  
The first program aims to execute a function in a Python script. Like in the
+
第一个程序旨在执行 Python 脚本中的函数。 就像在关于非常高级接口的部分中一样,Python 解释器不直接与应用程序交互(但这将在下一节中更改)。
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).
 
  
The code to run a function defined in a Python script is:
+
运行 Python 脚本中定义的函数的代码是:
  
 
<div class="highlight-c notranslate">
 
<div class="highlight-c notranslate">
第146行: 第100行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>#define PY_SSIZE_T_CLEAN
+
<syntaxhighlight lang="c">#define PY_SSIZE_T_CLEAN
#include &lt;Python.h&gt;
+
#include <Python.h>
  
 
int
 
int
第156行: 第110行:
 
     int i;
 
     int i;
  
     if (argc &lt; 3) {
+
     if (argc < 3) {
         fprintf(stderr,&quot;Usage: call pythonfile funcname [args]\n&quot;);
+
         fprintf(stderr,"Usage: call pythonfile funcname [args]\n");
 
         return 1;
 
         return 1;
 
     }
 
     }
第172行: 第126行:
 
         /* pFunc is a new reference */
 
         /* pFunc is a new reference */
  
         if (pFunc &amp;&amp; PyCallable_Check(pFunc)) {
+
         if (pFunc && PyCallable_Check(pFunc)) {
 
             pArgs = PyTuple_New(argc - 3);
 
             pArgs = PyTuple_New(argc - 3);
             for (i = 0; i &lt; argc - 3; ++i) {
+
             for (i = 0; i < argc - 3; ++i) {
 
                 pValue = PyLong_FromLong(atoi(argv[i + 3]));
 
                 pValue = PyLong_FromLong(atoi(argv[i + 3]));
 
                 if (!pValue) {
 
                 if (!pValue) {
 
                     Py_DECREF(pArgs);
 
                     Py_DECREF(pArgs);
 
                     Py_DECREF(pModule);
 
                     Py_DECREF(pModule);
                     fprintf(stderr, &quot;Cannot convert argument\n&quot;);
+
                     fprintf(stderr, "Cannot convert argument\n");
 
                     return 1;
 
                     return 1;
 
                 }
 
                 }
第188行: 第142行:
 
             Py_DECREF(pArgs);
 
             Py_DECREF(pArgs);
 
             if (pValue != NULL) {
 
             if (pValue != NULL) {
                 printf(&quot;Result of call: %ld\n&quot;, PyLong_AsLong(pValue));
+
                 printf("Result of call: %ld\n", PyLong_AsLong(pValue));
 
                 Py_DECREF(pValue);
 
                 Py_DECREF(pValue);
 
             }
 
             }
第195行: 第149行:
 
                 Py_DECREF(pModule);
 
                 Py_DECREF(pModule);
 
                 PyErr_Print();
 
                 PyErr_Print();
                 fprintf(stderr,&quot;Call failed\n&quot;);
+
                 fprintf(stderr,"Call failed\n");
 
                 return 1;
 
                 return 1;
 
             }
 
             }
第202行: 第156行:
 
             if (PyErr_Occurred())
 
             if (PyErr_Occurred())
 
                 PyErr_Print();
 
                 PyErr_Print();
             fprintf(stderr, &quot;Cannot find function \&quot;%s\&quot;\n&quot;, argv[2]);
+
             fprintf(stderr, "Cannot find function \"%s\"\n", argv[2]);
 
         }
 
         }
 
         Py_XDECREF(pFunc);
 
         Py_XDECREF(pFunc);
第209行: 第163行:
 
     else {
 
     else {
 
         PyErr_Print();
 
         PyErr_Print();
         fprintf(stderr, &quot;Failed to load \&quot;%s\&quot;\n&quot;, argv[1]);
+
         fprintf(stderr, "Failed to load \"%s\"\n", argv[1]);
 
         return 1;
 
         return 1;
 
     }
 
     }
     if (Py_FinalizeEx() &lt; 0) {
+
     if (Py_FinalizeEx() < 0) {
 
         return 120;
 
         return 120;
 
     }
 
     }
 
     return 0;
 
     return 0;
}</pre>
+
}</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
This code loads a Python script using <code>argv[1]</code>, and calls the function named
+
此代码使用 <code>argv[1]</code> 加载 Python 脚本,并调用 <code>argv[2]</code> 中命名的函数。 它的整数参数是 <code>argv</code> 数组的其他值。 如果你[[#compiling|编译并链接]]这个程序(我们调用完成的可执行文件'''call'''),并用它来执行一个Python脚本,例如:
in <code>argv[2]</code>. Its integer arguments are the other values of the <code>argv</code>
 
array. If you [[#compiling|<span class="std std-ref">compile and link</span>]] this program (let's call
 
the finished executable '''call'''), and use it to execute a Python
 
script, such as:
 
  
 
<div class="highlight-python notranslate">
 
<div class="highlight-python notranslate">
第231行: 第181行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>def multiply(a,b):
+
<syntaxhighlight lang="python">def multiply(a,b):
     print(&quot;Will compute&quot;, a, &quot;times&quot;, b)
+
     print("Will compute", a, "times", b)
 
     c = 0
 
     c = 0
 
     for i in range(0, a):
 
     for i in range(0, a):
 
         c = c + b
 
         c = c + b
     return c</pre>
+
     return c</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
then the result should be:
+
那么结果应该是:
  
 
<div class="highlight-shell-session notranslate">
 
<div class="highlight-shell-session notranslate">
第247行: 第197行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>$ call multiply multiply 3 2
+
<pre class="session">$ call multiply multiply 3 2
 
Will compute 3 times 2
 
Will compute 3 times 2
 
Result of call: 6</pre>
 
Result of call: 6</pre>
第254行: 第204行:
  
 
</div>
 
</div>
Although the program is quite large for its functionality, most of the code is
+
尽管该程序的功能相当庞大,但大部分代码用于 Python C 之间的数据转换以及错误报告。 关于嵌入 Python 的有趣部分始于
for data conversion between Python and C, and for error reporting. The
 
interesting part with respect to embedding Python starts with
 
  
 
<div class="highlight-c notranslate">
 
<div class="highlight-c notranslate">
第262行: 第210行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>Py_Initialize();
+
<syntaxhighlight lang="c">Py_Initialize();
 
pName = PyUnicode_DecodeFSDefault(argv[1]);
 
pName = PyUnicode_DecodeFSDefault(argv[1]);
 
/* Error checking of pName left out */
 
/* Error checking of pName left out */
pModule = PyImport_Import(pName);</pre>
+
pModule = PyImport_Import(pName);</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
After initializing the interpreter, the script is loaded using
+
初始化解释器后,使用 [[../../c-api/import#c|PyImport_Import()]] 加载脚本。 该例程需要一个 Python 字符串作为其参数,该字符串是使用 [[../../c-api/unicode#c|PyUnicode_FromString()]] 数据转换例程构造的。
[[../../c-api/import#c|<code>PyImport_Import()</code>]]. This routine needs a Python string as its argument,
 
which is constructed using the [[../../c-api/unicode#c|<code>PyUnicode_FromString()</code>]] data conversion
 
routine.
 
  
 
<div class="highlight-c notranslate">
 
<div class="highlight-c notranslate">
第279行: 第224行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>pFunc = PyObject_GetAttrString(pModule, argv[2]);
+
<syntaxhighlight lang="c">pFunc = PyObject_GetAttrString(pModule, argv[2]);
 
/* pFunc is a new reference */
 
/* pFunc is a new reference */
  
if (pFunc &amp;&amp; PyCallable_Check(pFunc)) {
+
if (pFunc && PyCallable_Check(pFunc)) {
 
     ...
 
     ...
 
}
 
}
Py_XDECREF(pFunc);</pre>
+
Py_XDECREF(pFunc);</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
Once the script is loaded, the name we're looking for is retrieved using
+
加载脚本后,我们将使用 [[../../c-api/object#c|PyObject_GetAttrString()]] 检索我们要查找的名称。 如果名称存在,并且返回的对象是可调用的,则可以安全地假设它是一个函数。 然后程序继续像往常一样构造一个参数元组。 然后使用以下命令调用 Python 函数:
[[../../c-api/object#c|<code>PyObject_GetAttrString()</code>]]. 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:
 
  
 
<div class="highlight-c notranslate">
 
<div class="highlight-c notranslate">
第300行: 第241行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>pValue = PyObject_CallObject(pFunc, pArgs);</pre>
+
<syntaxhighlight lang="c">pValue = PyObject_CallObject(pFunc, pArgs);</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
Upon return of the function, <code>pValue</code> is either <code>NULL</code> or it contains a
+
在函数返回时,<code>pValue</code> 要么是 <code>NULL</code>,要么包含对函数返回值的引用。 请务必在检查值后释放引用。
reference to the return value of the function. Be sure to release the reference
 
after examining the value.
 
  
  
第314行: 第253行:
  
 
<span id="extending-with-embedding"></span>
 
<span id="extending-with-embedding"></span>
== <span class="section-number">1.4. </span>Extending Embedded Python ==
+
== 1.4. 扩展嵌入式 Python ==
  
Until now, the embedded Python interpreter had no access to functionality from
+
到目前为止,嵌入式 Python 解释器无法访问应用程序本身的功能。 Python API 通过扩展嵌入式解释器来实现这一点。 也就是说,嵌入式解释器使用应用程序提供的例程进行扩展。 虽然听起来很复杂,但它并没有那么糟糕。 暂时忘记应用程序启动 Python 解释器。 相反,将应用程序视为一组子例程,并编写一些胶水代码让 Python 能够访问这些例程,就像编写普通的 Python 扩展一样。 例如:
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:
 
  
 
<div class="highlight-c notranslate">
 
<div class="highlight-c notranslate">
第329行: 第261行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>static int numargs=0;
+
<syntaxhighlight lang="c">static int numargs=0;
  
 
/* Return the number of arguments of the application command line */
 
/* Return the number of arguments of the application command line */
第335行: 第267行:
 
emb_numargs(PyObject *self, PyObject *args)
 
emb_numargs(PyObject *self, PyObject *args)
 
{
 
{
     if(!PyArg_ParseTuple(args, &quot;:numargs&quot;))
+
     if(!PyArg_ParseTuple(args, ":numargs"))
 
         return NULL;
 
         return NULL;
 
     return PyLong_FromLong(numargs);
 
     return PyLong_FromLong(numargs);
第341行: 第273行:
  
 
static PyMethodDef EmbMethods[] = {
 
static PyMethodDef EmbMethods[] = {
     {&quot;numargs&quot;, emb_numargs, METH_VARARGS,
+
     {"numargs", emb_numargs, METH_VARARGS,
     &quot;Return the number of arguments received by the process.&quot;},
+
     "Return the number of arguments received by the process."},
 
     {NULL, NULL, 0, NULL}
 
     {NULL, NULL, 0, NULL}
 
};
 
};
  
 
static PyModuleDef EmbModule = {
 
static PyModuleDef EmbModule = {
     PyModuleDef_HEAD_INIT, &quot;emb&quot;, NULL, -1, EmbMethods,
+
     PyModuleDef_HEAD_INIT, "emb", NULL, -1, EmbMethods,
 
     NULL, NULL, NULL, NULL
 
     NULL, NULL, NULL, NULL
 
};
 
};
第354行: 第286行:
 
PyInit_emb(void)
 
PyInit_emb(void)
 
{
 
{
     return PyModule_Create(&amp;EmbModule);
+
     return PyModule_Create(&EmbModule);
}</pre>
+
}</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
Insert the above code just above the <code>main()</code> function. Also, insert the
+
<code>main()</code> 函数的正上方插入上述代码。 此外,在调用 [[../../c-api/init#c|Py_Initialize()]] 之前插入以下两个语句:
following two statements before the call to [[../../c-api/init#c|<code>Py_Initialize()</code>]]:
 
  
 
<div class="highlight-c notranslate">
 
<div class="highlight-c notranslate">
第367行: 第298行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>numargs = argc;
+
<syntaxhighlight lang="c">numargs = argc;
PyImport_AppendInittab(&quot;emb&quot;, &amp;PyInit_emb);</pre>
+
PyImport_AppendInittab("emb", &PyInit_emb);</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
These two lines initialize the <code>numargs</code> variable, and make the
+
这两行初始化 <code>numargs</code> 变量,并使嵌入式 Python 解释器可以访问 <code>emb.numargs()</code> 函数。 通过这些扩展,Python 脚本可以执行以下操作:
<code>emb.numargs()</code> function accessible to the embedded Python interpreter.
 
With these extensions, the Python script can do things like
 
  
 
<div class="highlight-python notranslate">
 
<div class="highlight-python notranslate">
第381行: 第310行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>import emb
+
<syntaxhighlight lang="python">import emb
print(&quot;Number of arguments&quot;, emb.numargs())</pre>
+
print("Number of arguments", emb.numargs())</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
In a real application, the methods will expose an API of the application to
+
在实际应用程序中,这些方法会将应用程序的 API 公开给 Python。
Python.
 
  
  
第395行: 第323行:
  
 
<span id="embeddingincplusplus"></span>
 
<span id="embeddingincplusplus"></span>
== <span class="section-number">1.5. </span>Embedding Python in C++ ==
+
== 1.5. C++ 中嵌入 Python ==
  
It is also possible to embed Python in a C++ program; precisely how this is done
+
也可以在 C++ 程序中嵌入 Python; 具体如何完成取决于所使用的 C++ 系统的细节; 通常,您需要用 C++ 编写主程序,并使用 C++ 编译器来编译和链接您的程序。 无需使用 C++ 重新编译 Python 本身。
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++.
 
  
  
第407行: 第332行:
  
 
<span id="compiling"></span>
 
<span id="compiling"></span>
== <span class="section-number">1.6. </span>Compiling and Linking under Unix-like systems ==
+
== 1.6. 在类 Unix 系统下编译和链接 ==
  
It is not necessarily trivial to find the right flags to pass to your
+
找到传递给编译器(和链接器)以将 Python 解释器嵌入到应用程序中的正确标志并不一定是微不足道的,特别是因为 Python 需要加载实现为 C 动态扩展的库模块(<code>.so</code>文件)链接到它。
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 (<code>.so</code> files) linked against
 
it.
 
  
To find out the required compiler and linker flags, you can execute the
+
要找出所需的编译器和链接器标志,您可以执行作为安装过程的一部分生成的 <code>pythonX.Y-config</code> 脚本(也可以使用 <code>python3-config</code> 脚本)。 此脚本有多个选项,其中以下选项对您直接有用:
<code>pythonX.Y-config</code> script which is generated as part of the
 
installation process (a <code>python3-config</code> script may also be
 
available). This script has several options, of which the following will
 
be directly useful to you:
 
  
 
<ul>
 
<ul>
<li><p><code>pythonX.Y-config --cflags</code> will give you the recommended flags when
+
<li><p><code>pythonX.Y-config --cflags</code> 会在编译时给你推荐的标志:</p>
compiling:</p>
 
 
<div class="highlight-shell-session notranslate">
 
<div class="highlight-shell-session notranslate">
  
 
<div class="highlight">
 
<div class="highlight">
  
<pre>$ /opt/bin/python3.4-config --cflags
+
<pre class="session">$ /opt/bin/python3.4-config --cflags
 
-I/opt/include/python3.4m -I/opt/include/python3.4m -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes</pre>
 
-I/opt/include/python3.4m -I/opt/include/python3.4m -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes</pre>
  
第434行: 第350行:
  
 
</div></li>
 
</div></li>
<li><p><code>pythonX.Y-config --ldflags</code> will give you the recommended flags when
+
<li><p><code>pythonX.Y-config --ldflags</code> 会在链接时给你推荐的标志:</p>
linking:</p>
 
 
<div class="highlight-shell-session notranslate">
 
<div class="highlight-shell-session notranslate">
  
 
<div class="highlight">
 
<div class="highlight">
  
<pre>$ /opt/bin/python3.4-config --ldflags
+
<pre class="session">$ /opt/bin/python3.4-config --ldflags
 
-L/opt/lib/python3.4/config-3.4m -lpthread -ldl -lutil -lm -lpython3.4m -Xlinker -export-dynamic</pre>
 
-L/opt/lib/python3.4/config-3.4m -lpthread -ldl -lutil -lm -lpython3.4m -Xlinker -export-dynamic</pre>
  
第449行: 第364行:
 
<div class="admonition note">
 
<div class="admonition note">
  
注解
+
笔记
  
To avoid confusion between several Python installations (and especially
+
为避免多个 Python 安装之间的混淆(尤其是系统 Python 和您自己编译的 Python 之间),建议您使用 <code>pythonX.Y-config</code> 的绝对路径,如上例所示。
between the system Python and your own compiled Python), it is recommended
 
that you use the absolute path to <code>pythonX.Y-config</code>, as in the above
 
example.
 
  
  
 
</div>
 
</div>
If this procedure doesn't work for you (it is not guaranteed to work for
+
如果此过程对您不起作用(不保证适用于所有类 Unix 平台;但是,我们欢迎 [[../../bugs#reporting-bugs|错误报告]] ),您将必须阅读您的系统关于动态链接和/或检查 Python <code>Makefile</code>(使用 [[../../library/sysconfig#sysconfig|sysconfig.get_makefile_filename()]] 找到它的位置)和编译选项。 在这种情况下,[[../../library/sysconfig#module-sysconfig|sysconfig]] 模块是一个有用的工具,可以以编程方式提取您想要组合在一起的配置值。 例如:
all Unix-like platforms; however, we welcome [[../../bugs#reporting-bugs|<span class="std std-ref">bug reports</span>]])
 
you will have to read your system's documentation about dynamic linking and/or
 
examine Python's <code>Makefile</code> (use [[../../library/sysconfig#sysconfig|<code>sysconfig.get_makefile_filename()</code>]]
 
to find its location) and compilation
 
options. In this case, the [[../../library/sysconfig#module-sysconfig|<code>sysconfig</code>]] module is a useful tool to
 
programmatically extract the configuration values that you will want to
 
combine together. For example:
 
  
 
<div class="highlight-pycon notranslate">
 
<div class="highlight-pycon notranslate">
第471行: 第376行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>&gt;&gt;&gt; import sysconfig
+
<syntaxhighlight lang="pycon">>>> import sysconfig
&gt;&gt;&gt; sysconfig.get_config_var('LIBS')
+
>>> sysconfig.get_config_var('LIBS')
 
'-lpthread -ldl  -lutil'
 
'-lpthread -ldl  -lutil'
&gt;&gt;&gt; sysconfig.get_config_var('LINKFORSHARED')
+
>>> sysconfig.get_config_var('LINKFORSHARED')
'-Xlinker -export-dynamic'</pre>
+
'-Xlinker -export-dynamic'</syntaxhighlight>
  
 
</div>
 
</div>
第484行: 第389行:
  
 
</div>
 
</div>
 +
<div class="clearer">
  
[[Category:Python 3.9 中文文档]]
+
 
 +
 
 +
</div>
 +
 
 +
[[Category:Python 3.9 文档]]

2021年10月31日 (日) 04:50的最新版本

1. 在另一个应用程序中嵌入 Python

前面的章节讨论了如何扩展 Python,即如何通过附加 C 函数库来扩展 Python 的功能。 也可以反过来做:通过在其中嵌入 Python 来丰富您的 C/C++ 应用程序。 嵌入使您的应用程序能够使用 Python 而不是 C 或 C++ 来实现应用程序的某些功能。 这可以用于许多目的; 一个例子是允许用户通过用 Python 编写一些脚本来根据他们的需要定制应用程序。 如果某些功能可以更轻松地用 Python 编写,您也可以自己使用它。

嵌入 Python 类似于扩展它,但不完全相同。 不同的是,当你扩展 Python 时,应用程序的主程序仍然是 Python 解释器,而如果你嵌入 Python,主程序可能与 Python 无关——相反,应用程序的某些部分偶尔会调用 Python 解释器运行一些 Python 代码。

因此,如果您要嵌入 Python,您将提供自己的主程序。 这个主程序必须做的一件事是初始化 Python 解释器。 至少,您必须调用函数 Py_Initialize()。 有一些可选调用可以将命令行参数传递给 Python。 然后,您可以从应用程序的任何部分调用解释器。

有几种不同的调用解释器的方法:您可以将包含 Python 语句的字符串传递给 PyRun_SimpleString(),或者您可以将 stdio 文件指针和文件名(仅用于在错误消息中识别)传递给PyRun_SimpleFile()。 您还可以调用前面章节中描述的低级操作来构造和使用 Python 对象。

也可以看看

Python/C API 参考手册
本手册中给出了 Python 的 C 接口的详细信息。 在这里可以找到大量必要的信息。


1.1. 非常高级的嵌入

嵌入 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;
}

Py_SetProgramName() 函数应该在 Py_Initialize() 之前调用,以通知解释器有关 Python 运行时库的路径。 接下来,使用 Py_Initialize() 初始化 Python 解释器,然后执行打印日期和时间的硬编码 Python 脚本。 之后, Py_FinalizeEx() 调用关闭解释器,然后程序结束。 在实际程序中,您可能希望从其他来源(可能是文本编辑器例程、文件或数据库)获取 Python 脚本。 使用 PyRun_SimpleFile() 函数可以更好地从文件中获取 Python 代码,这样可以省去分配内存空间和加载文件内容的麻烦。


1.2. 超越非常高级的嵌入:概述

高级接口使您能够从应用程序执行任意 Python 代码段,但至少可以说交换数据值非常麻烦。 如果需要,您应该使用较低级别的调用。 以必须编写更多 C 代码为代价,您几乎可以实现任何目标。

应该注意的是,尽管意图不同,但扩展 Python 和嵌入 Python 是完全相同的活动。 前几章中讨论的大多数主题仍然有效。 为了说明这一点,请考虑从 Python 到 C 的扩展代码的真正作用:

  1. 将数据值从 Python 转换为 C,
  2. 使用转换后的值对 C 例程执行函数调用,以及
  3. 将调用中的数据值从 C 转换为 Python。

嵌入 Python 时,接口代码执行以下操作:

  1. 将数据值从 C 转换为 Python,
  2. 使用转换后的值对 Python 接口例程执行函数调用,以及
  3. 将来自 Python 调用的数据值转换为 C。

如您所见,数据转换步骤只是交换以适应跨语言传输的不同方向。 唯一的区别是您在两次数据转换之间调用的例程。 扩展时调用 C 例程,嵌入时调用 Python 例程。

本章不会讨论如何将数据从 Python 转换为 C,反之亦然。 此外,假定已理解正确使用引用和处理错误。 由于这些方面与扩展解释器没有区别,因此您可以参考前面的章节以获取所需信息。


1.3. 纯嵌入

第一个程序旨在执行 Python 脚本中的函数。 就像在关于非常高级接口的部分中一样,Python 解释器不直接与应用程序交互(但这将在下一节中更改)。

运行 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;
}

此代码使用 argv[1] 加载 Python 脚本,并调用 argv[2] 中命名的函数。 它的整数参数是 argv 数组的其他值。 如果你编译并链接这个程序(我们调用完成的可执行文件call),并用它来执行一个Python脚本,例如:

def multiply(a,b):
    print("Will compute", a, "times", b)
    c = 0
    for 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 和 C 之间的数据转换以及错误报告。 关于嵌入 Python 的有趣部分始于

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

初始化解释器后,使用 PyImport_Import() 加载脚本。 该例程需要一个 Python 字符串作为其参数,该字符串是使用 PyUnicode_FromString() 数据转换例程构造的。

pFunc = PyObject_GetAttrString(pModule, argv[2]);
/* pFunc is a new reference */

if (pFunc && PyCallable_Check(pFunc)) {
    ...
}
Py_XDECREF(pFunc);

加载脚本后,我们将使用 PyObject_GetAttrString() 检索我们要查找的名称。 如果名称存在,并且返回的对象是可调用的,则可以安全地假设它是一个函数。 然后程序继续像往常一样构造一个参数元组。 然后使用以下命令调用 Python 函数:

pValue = PyObject_CallObject(pFunc, pArgs);

在函数返回时,pValue 要么是 NULL,要么包含对函数返回值的引用。 请务必在检查值后释放引用。


1.4. 扩展嵌入式 Python

到目前为止,嵌入式 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);
}

main() 函数的正上方插入上述代码。 此外,在调用 Py_Initialize() 之前插入以下两个语句:

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

这两行初始化 numargs 变量,并使嵌入式 Python 解释器可以访问 emb.numargs() 函数。 通过这些扩展,Python 脚本可以执行以下操作:

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

在实际应用程序中,这些方法会将应用程序的 API 公开给 Python。


1.5. 在 C++ 中嵌入 Python

也可以在 C++ 程序中嵌入 Python; 具体如何完成取决于所使用的 C++ 系统的细节; 通常,您需要用 C++ 编写主程序,并使用 C++ 编译器来编译和链接您的程序。 无需使用 C++ 重新编译 Python 本身。


1.6. 在类 Unix 系统下编译和链接

找到传递给编译器(和链接器)以将 Python 解释器嵌入到应用程序中的正确标志并不一定是微不足道的,特别是因为 Python 需要加载实现为 C 动态扩展的库模块(.so文件)链接到它。

要找出所需的编译器和链接器标志,您可以执行作为安装过程的一部分生成的 pythonX.Y-config 脚本(也可以使用 python3-config 脚本)。 此脚本有多个选项,其中以下选项对您直接有用:

  • pythonX.Y-config --cflags 会在编译时给你推荐的标志:

    $ /opt/bin/python3.4-config --cflags
    -I/opt/include/python3.4m -I/opt/include/python3.4m -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes
  • pythonX.Y-config --ldflags 会在链接时给你推荐的标志:

    $ /opt/bin/python3.4-config --ldflags
    -L/opt/lib/python3.4/config-3.4m -lpthread -ldl -lutil -lm -lpython3.4m -Xlinker -export-dynamic

笔记

为避免多个 Python 安装之间的混淆(尤其是系统 Python 和您自己编译的 Python 之间),建议您使用 pythonX.Y-config 的绝对路径,如上例所示。


如果此过程对您不起作用(不保证适用于所有类 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'