(Python编程)基本的内嵌技术

标签: python编程dictionarystringnamespacesmodule
2871人阅读 评论(2) 收藏 举报
分类:
Programming Python, 3rd Edition 翻译
最新版本见wiki:http://wiki.woodpecker.org.cn/moin/PP3eD
欢迎参与翻译与修订。

23.3. Basic Embedding Techniques

23.3. 基本的内嵌技术

As you can probably tell from the preceding overview, there is much flexibility in the embedding domain. To illustrate common embedding techniques in action, this section presents a handful of short C programs that run Python code in one form or another. Most of these examples make use of the simple Python module file shown in Example 23-1.

在前面的概览一节中你已经知道,内嵌领域的灵活性很大。 本节列举了几个简短的C程序, 它们以一种形式或另一种形式运行Python代码, 来演示实际的通用的内嵌技术。 多数例子使用了这个简单的Python模块,见Example 23-1。(头部注释为:“C代码以内嵌模式运行本模块中的Python代码。 这样一个文件可以更改而不必更改C代码层。 这只是标准的Python代码(C代码做转换工作)。 你也可以运行标准模块如string中的代码。”)

Example 23-1. PP3E/Integrate/Embed/Basics/usermod.py

#########################################################
# C runs Python code in this module in embedded mode.
# Such a file can be changed without changing the C layer.
# There is just standard Python code (C does conversions).
# You can also run code in standard modules like string.
#########################################################

message = 'The meaning of life...'

def transform(input):
    input = input.replace('life', 'Python')
    return input.upper( )

If you know any Python at all, you probably know that this file defines a string and a function; the function returns whatever it is passed with string substitution and uppercase conversions applied. It's easy to use from Python:

如果你知道一点Python, 你可能知道, 这个文件定义了一个字符串与一个函数;函数将输入串替换并转换成大写后返回。 这在Python中很容易使用:

.../PP3E/Integrate/Embed/Basics$ python
>>> import usermod                                      # import a module
>>> usermod.message                                     # fetch a string
'The meaning of life...'
>>> usermod.transform(usermod.message)                  # call a function
'THE MEANING OF PYTHON...'

With a little Python API wizardry, it's not much more difficult to use this module the same way in C.

利用一点Python API的魔力, 在C语言中以同样的方式使用这个模块也不是很难。

23.3.1. Running Simple Code Strings

23.3.1. 运行简单代码串

Perhaps the simplest way to run Python code from C is by calling the PyRun_SimpleString API function. With it, C programs can execute Python programs represented as C character string arrays. This call is also very limited: all code runs in the same namespace (the module _ _main_ _), the code strings must be Python statements (not expressions), and there is no direct way to communicate inputs or outputs with the Python code run.

从C语言运行Python代码,最简单的方法可能是, 调用PyRun_SimpleString API 函数。 使用它,C程序可以执行用C字符串数组表示的Python程序。 这个调用的限制也很大: 所有化码运行于同一命名空间(模块_ _main_ _), 代码串必须是Python语句(不是表达式), 也没有直接的办法可以与运行的Python代码通信, 进行输入与输出。

Still, it's a simple place to start. Moreover, when augmented with an imported C extension module that the embedded Python code can use to communicate with the enclosing C layer, this technique can satisfy many embedding goals. To demonstrate the basics, the C program in Example 23-2 runs Python code to accomplish the same results as the interactive session listed in the prior section.

但它仍是一个好的开始。 另外,如果在参数中导入C扩展模块, 通过该扩展模块, 内嵌的Python代码就可以与外面的C语言层通信了, 这一技术可以满足许多内嵌目标。 C程序Example 23-2 演示了这一基本方法, 它运行Python代码, 产生与上面交互式运行相同的结果。

Example 23-2. PP3E/Integrate/Embed/Basics/embed-simple.c

/*******************************************************
 * simple code strings: C acts like the interactive
 * prompt, code runs in _ _main_ _, no output sent to C;
 *******************************************************/
#include <Python.h>    /* standard API def */

main( ) {
    printf("embed-simple/n");
    Py_Initialize( );
    PyRun_SimpleString("import usermod");                /* load .py file */
    PyRun_SimpleString("print usermod.message");         /* on Python path */
    PyRun_SimpleString("x = usermod.message");           /* compile and run */
    PyRun_SimpleString("print usermod.transform(x)");
}

The first thing you should notice here is that when Python is embedded, C programs always call Py_Initialize to initialize linked-in Python libraries before using any other API functions. The rest of this code is straightforwardC submits hardcoded strings to Python that are roughly what we typed interactively. Internally, PyRun_SimpleString invokes the Python compiler and interpreter to run the strings sent from C; as usual, the Python compiler is always available in systems that contain Python.

首先请注意, 当Python内嵌时, 在调用任何其它API函数之前, C程序总是先调用Py_Initialize, 来初始化链接的Python库。 代码余下部分 与交互方式下打字输入差不多, 直截了当地向Python提交硬编码的字符串。 在内部, PyRun_SimpleString调用Python编译器和解释器, 来运行C语言传入的字符串; 通常,在安装了Python的系统上, Python编译器总是可用的。

23.3.1.1. Compiling and running
23.3.1.1. 编译与运行

To build a standalone executable from this C source file, you need to link its compiled form with the Python library file. In this chapter, "library" usually means the binary library file that is generated when Python is compiled, not the Python source code library.

为了从这个C语言源文件构建独立的执行程序, 编译后需要链接Python库文件。 在本章中,“库”通常指Python编译后生成的二进制库文件,而不是Python源代码库。

Today, everything in Python that you need in C is compiled into a single Python library file when the interpreter is built (e.g., libpython2.4.dll on Cygwin). The program's main function comes from your C code, and depending on your platform and the extensions installed in your Python, you may also need to link any external libraries referenced by the Python library.

目前,当Python解释器构建时,你在C语言中所需的所有Python的东西,会被编译到一个Python库文件 (在Cygwin上即libpython2.4.dll)。 程序的main函数来自于你的C语言代码, 与你的平台和Python安装的扩展有关, 你可能还需要链接Python库所引用的外部库。

Assuming no extra extension libraries are needed, Example 23-3 is a minimal makefile for building the C program in Example 23-2 under Cygwin on Windows. Again, makefile details vary per platform, but see Python manuals for hints. This makefile uses the Python include-files path to find Python.h in the compile step and adds the Python library file to the final link step to make API calls available to the C program.

假设不需要额外的扩展库,Example 23-3 是在Windows的Cygwin下构建Example 23-2 C程序的最小的make文件。同样,make文件的细节随平台而变,请查看Python手册获取帮助。 这个make文件在编译时使用Python包含路径来寻找Python.h, 并在最后的链接步骤中添加Python库文件,使API调用在C程序中可用。

Example 23-3. PP3E/Integrate/Embed/Basics/makefile.1

# a Cygwin makefile that builds a C executable that embeds
# Python, assuming no external module libs must be linked in;
# uses Python header files, links in the Python lib file;
# both may be in other dirs (e.g., /usr) in your install;

PYLIB = /usr/bin
PYINC = /usr/include/python2.4

embed-simple: embed-simple.o
        gcc embed-simple.o -L$(PYLIB) -lpython2.4 -g -o embed-simple

embed-simple.o: embed-simple.c
        gcc embed-simple.c -c -g -I$(PYINC)

To build a program with this file, launch make on it as usual:

用这个文件构建程序时,按通常方式运行make:

.../PP3E/Integrate/Embed/Basics$ make -f makefile.1
gcc embed-simple.c -c -g -I/usr/include/python2.4
gcc embed-simple.o -L/usr/bin -lpython2.4 -g -o embed-simple

Things may not be quite this simple in practice, though, at least not without some coaxing. The makefile in Example 23-4 is the one I actually used to build all of this section's C programs on Cygwin.

但是,实际可能不这么容易,会需要一点技术。 Example 23-4 是我在Cygwin下实际使用的make文件,用于构建本章所有的C程序。

Example 23-4. PP3E/Integrate/Embed/Basics/makefile.basics

# cygwin makefile to build all 5
# basic embedding examples at once

PYLIB = /usr/bin
PYINC = /usr/include/python2.4

BASICS = embed-simple.exe   /
         embed-string.exe   /
         embed-object.exe   /
         embed-dict.exe     /
         embed-bytecode.exe

all:    $(BASICS)

embed%.exe: embed%.o
        gcc embed$*.o -L$(PYLIB) -lpython2.4 -g -o $@

embed%.o: embed%.c
        gcc embed$*.c -c -g -I$(PYINC)

clean:
        rm -f *.o *.pyc $(BASICS) core

On some platforms, you may need to also link in other libraries because the Python library file used may have been built with external dependencies enabled and required. In fact, you may have to link in arbitrarily many more externals for your Python library, and frankly, chasing down all the linker dependencies can be tedious. Required libraries may vary per platform and Python install, so there isn't a lot of advice I can offer to make this process simple (this is C, after all). The standard C development techniques will apply.

在有些平台上,你可能需要链接其它库,因为用到的Python库文件依赖和要求外部库。 实际上,你的Python库可能要求你必须链接许多外部库, 并且坦白说,找出所有的链接依赖关系是很烦的。 要求的库随平台和安装的Python不同而有很大不同, 所以我也没办法使这个过程简单点(毕竟这是C语言)。 请运用标准的C语言开发技术来解决。

One thing to note is that on some platforms, if you're going to do much embedding work and you run into external dependency issues, you might want to build Python on your machine from its source with all unnecessary extensions disabled in the Modules/Setup file (or the top-level setup.py Distutils script in more recent releases). This produces a Python library with minimal external requirements, which links much more easily.

注意,在有些平台上, 如果你要做许多内嵌工作, 并且你碰到了外部依赖问题, 你可能需要在你的机器上, 从源码构建Python, 并在Modules/Setup文件(或在新版本中的顶层setup.py Distutils脚本)中, 禁用所有不必要的扩展。 这样生成的Python库,具有最小的外部依赖,链接时会方便许多。

For example, if your embedded code won't be building GUIs, Tkinter can simply be removed from the library; see the README file at the top of Python's source distribution for details. You can also find a list of external libraries referenced from your Python in the generated makefiles located in the Python source tree. In any event, the good news is that you need to resolve linker dependencies only once.

例如,如果你的内嵌代码不会构建GUI,就可以直接从库里面移除Tkinter; 详情请参阅Python源代码树中顶层的README文件。 在Python源代码树中,在生成的make文件中, 你也可以找到你的Python引用外部库的列表。 无论如何,好消息是链接依赖问题你只需解决一次。

Once you've gotten the makefile to work, run it to build the C program with Python libraries linked in:

一旦make文件可以工作,运行它来构建C程序,并链接Python库:

.../PP3E/Integrate/Embed/Basics$ make -f makefile.basics clean 
rm -f *.o *.pyc embed-simple.exe embed-string.exe embed-object.exe embed-dict.ex
e embed-bytecode.exe core

.../PP3E/Integrate/Embed/Basics$ make -f makefile.basics 
gcc embed-simple.c -c -g -I/usr/include/python2.4
gcc embed-simple.o -L/usr/bin -lpython2.4 -g -o embed-simple.exe
 ...lines deleted...
gcc embed-bytecode.c -c -g -I/usr/include/python2.4
gcc embed-bytecode.o -L/usr/bin -lpython2.4 -g -o embed-bytecode.exe
rm embed-dict.o embed-object.o embed-simple.o embed-bytecode.o embed-string.o

After building, run the resulting C program as usual, regardless of how this works in your platform:[*]

构建之后,运行生成的C程序 (不同平台方法不同 [*]):

[*] Under Python 2.4 and Cygwin on Windows, I had to first set my PYTHONPATH to include the current directory in order to run the embedding examples under Python 2.4 and Cygwin, with the shell command export PYTHONPATH=.. I also had to use the shell command ./embed-simple to execute the program due to my system path setting. Your mileage may vary; if you have trouble, try running the embedded Python commands import sys and print sys.path from C to see what Python's path looks like, and take a look at the Python/C API manual for more on path configuration for embedded applications.

[*] 在Windows的Cygwin和Python2.4下,为了运行内嵌的例程, 我必须先使用shell命令export PYTHONPATH=., 来设置PYTHONPATH包含当前目录。 同时,由于我的系统路径设置关系,我必须使用shell命令./embed-simple来执行程序。 你的方法可以不同;如果你有麻烦,试试在C语言中运行内嵌Python命令 import sysprint sys.path, 看看Python路径是怎样的, 更多内嵌应用的路径配置信息,请查阅 Python/C API手册。

.../PP3E/Integrate/Embed/Basics$ embed-simple
embed-simple
The meaning of life...
THE MEANING OF PYTHON...

Most of this output is produced by Python print statements sent from C to the linked-in Python library. It's as if C has become an interactive Python programmer.

大多数的输出是由Python print语句产生的, 该语句由C语言发送到链入的Python库。 就像C成为了一个交互式Python程序员。

Naturally, strings of Python code run by C probably would not be hardcoded in a C program file like this. They might instead be loaded from a text file or GUI, extracted from HTML or XML files, fetched from a persistent database or socket, and so on. With such external sources, the Python code strings that are run from C could be changed arbitrarily without having to recompile the C program that runs them. They may even be changed onsite, and by end users of a system. To make the most of code strings, though, we need to move on to more flexible API tools.

当然,C语言运行的Python代码串, 可能不是这样在C语言文件中硬编码的。 它们可以是从一个文本文件或GUI读取、 从HTML或XML文件中提取、 从数据库或socket获取、等等。 使用这样的外部源, 就可以任意更改C语言运行的Python代码串, 而不必重新编译运行它们的C程序。 它们甚至可以由系统的最终用户现场更改。 但是,为充分利用代码串,我们需要继续前进,学习更灵活的API工具。

23.3.2. Running Code Strings with Results and Namespaces

23.3.2. 带结果和名字空间运行代码串

Example 23-5 uses the following API calls to run code strings that return expression results back to C:

Example 23-5 使用下面的API调用来运行代码串,并返回表达式的结果到C:


Py_Initialize

Initializes linked-in Python libraries as before

同上,初始化链入的Python库


PyImport_ImportModule

Imports a Python module and returns a pointer to it

导入一个Python模块并返回它的指针


PyModule_GetDict

Fetches a module's attribute dictionary object

获取一个模块的属性字典对象


PyRun_String

Runs a string of code in explicit namespaces

在指定的名字空间内运行代码串


PyObject_SetAttrString

Assigns an object attribute by namestring

通过namestring为一个对象的属性赋值


PyArg_Parse

Converts a Python return value object to C form

将Python返回值对象转换为C语言形式

The import calls are used to fetch the namespace of the usermod module listed in Example 23-1 earlier so that code strings can be run there directly (and will have access to names defined in that module without qualifications). Py_Import_ImportModule is like a Python import statement, but the imported module object is returned to C; it is not assigned to a Python variable name. As a result, it's probably more similar to the Python _ _import_ _ built-in function.

导入调用用来获取前面 Example 23-1 中所列模块usermod的名字空间, 然后,代码串就可以直接在那里运行 (并且可以访问那个模块内定义的名字,无需修饰)。 PyImport_ImportModule就像Python的import语句, 但是导入的模块对象会返回给C语言; 它并不赋值给一个Python变量名。 结果是,它可能更像Python的_ _import_ _内置函数。

The PyRun_String call is the one that actually runs code here, though. It takes a code string, a parser mode flag, and dictionary object pointers to serve as the global and local namespaces for running the code string. The mode flag can be Py_eval_input to run an expression, or Py_file_input to run a statement; when running an expression, the result of evaluating the expression is returned from this call (it comes back as a PyObject* object pointer). The two namespace dictionary pointer arguments allow you to distinguish global and local scopes, but they are typically passed the same dictionary such that code runs in a single namespace.[*]

PyRun_String调用是实际运行代码的地方。 它接受一个代码串,一个分析模式标志, 和两个字典对象指针, 这两个字典分别作为代码串运行的全局和局部的名字空间。 模式标志可以是Py_eval_input, 来运行一个表达式, 或者Py_file_input, 来运行一个语句; 当运行表达式时,这个调用会返回表达式的计算结果(返回一个对象指针PyObject*)。 两个名字空间字典指针参数允许你区分全局和局部作用域, 但是它们一般是同一个字典,如此代码会运行于单一的名字空间。 [*]

(译注:难道就不能不分表达式和语句吗?)

[*] A related function lets you run files of code but is not demonstrated in this chapter: PyObject* PyRun_File(FILE *fp, char *filename, mode, globals, locals). Because you can always load a file's text and run it as a single code string with PyRun_String, the PyRun_File call is not always necessary. In such multiline code strings, the /n character terminates lines and indentation groups blocks as usual.

[*] 有一个相关的函数可以让你运行代码文件, 但是本章没有示例: PyObject* PyRun_File(FILE *fp, char *filename, mode, globals, locals)。 因为你总是可以读取文件内容,并作为一个代码串, 用PyRun_String来运行它, 所以PyRun_File并不总是必要的。 在多行代码串的情况下, 可用/n字符分隔行, 并照常缩进代码块。

Example 23-5. PP3E/Integrate/Embed/Basics/embed-string.c

/* code-strings with results and namespaces 
 */

#include <Python.h>

main( ) {
    char *cstr;
    PyObject *pstr, *pmod, *pdict;
    printf("embed-string/n");
    Py_Initialize( );

    /* get usermod.message */
    pmod  = PyImport_ImportModule("usermod");
    pdict = PyModule_GetDict(pmod);
    pstr  = PyRun_String("message", Py_eval_input, pdict, pdict);

    /* convert to C */
    PyArg_Parse(pstr, "s", &cstr);
    printf("%s/n", cstr);

    /* assign usermod.X */
    PyObject_SetAttrString(pmod, "X", pstr);

    /* print usermod.transform(X) */
    (void) PyRun_String("print transform(X)", Py_file_input, pdict, pdict);
    Py_DECREF(pmod);
    Py_DECREF(pstr);
}

When compiled and run, this file produces the same result as its predecessor:

编译后运行的结果与上个程序是一样的:

.../PP3E/Integrate/Embed/Basics$ embed-string
embed-string
The meaning of life...
THE MEANING OF PYTHON...

But very different work goes into producing this output. This time, C fetches, converts, and prints the value of the Python module's message attribute directly by running a string expression and assigning a global variable (X) within the module's namespace to serve as input for a Python print statement string.

但是为了产生这个输出,所做的工作是很不一样的。 这一次,通过运行一个字符串表达式, C语言直接获取,转换并打印Python模块的message属性, 并在模块的名字空间中, 对一个全局变量(X)赋值, 该变量再作为Python print语句的输入。

Because the string execution call in this version lets you specify namespaces, you can better partition the embedded code your system runseach grouping can have a distinct namespace to avoid overwriting other groups' variables. And because this call returns a result, you can better communicate with the embedded code; expression results are outputs, and assignments to globals in the namespace in which code runs can serve as inputs.

因为该版本的字符串运行函数可以让你指定名字空间, 你可以更好地隔离系统中运行的内嵌代码, 每个代码组都可以有一个独特的名字空间, 避免改写其它组的变量。 并且,这个函数返回一个结果, 让你可以更好地与内嵌代码通信; 表达式结果是输出, 在代码运行的名字空间内, 它又赋值给全局变量, 而全局变量可以作为输入。

Before we move on, I need to explain two coding issues here. First, this program also decrements the reference count on objects passed to it from Python, using the Py_DECREF call introduced in Chapter 22. These calls are not strictly needed here (the objects' space is reclaimed when the programs exits anyhow), but they demonstrate how embedding interfaces must manage reference counts when Python passes their ownership to C. If this was a function called from a larger system, for instance, you would generally want to decrement the count to allow Python to reclaim the objects.

在我们继续前进前,我要解释一下,这里有两个代码问题。 首先,这个程序使用 第22章 介绍的Py_DECREF函数, 减小了从Python传来的对象的引用计数。 这些调用在此并非严格需要 (无论如何,对象占用空间在程序退出时会收回), 但是它们演示了, 当Python将对象所有权传递到C语言时, 内嵌接口必须如何管理引用计数。 如果这是个大型系统中的函数调用,通常你需要减小计数,以允许Python收回对象。

Second, in a realistic program, you should generally test the return values of all the API calls in this program immediately to detect errors (e.g., import failure). Error tests are omitted in this section's example to keep the code simple, but they will appear in later code listings and should be included in your programs to make them more robust.

第二,在一个实际的程序中, 你一般应该立即检查程序中 所有API调用的返回值, 以检测错误(例如导入失败)。 本节中的例子省略了错误检查, 以保持代码清晰, 但是它们将会在以后的代码清单中出现, 并且应该包含在你的程序中,使之更健壮。

23.3.3. Calling Python Objects

23.3.3. 调用Python对象

The last two sections dealt with running strings of code, but it's easy for C programs to deal in terms of Python objects too. Example 23-6 accomplishes the same task as Examples 23-2 and 23-5, but it uses other API tools to interact with objects in the Python module directly:

上面两节讨论的是代码串的运行, 但是对C程序来说, 按Python对象运行也很容易。 Example 23-6 实现Examples 23-223-5相同的任务, 但是使用其它的API工具, 来与Python模块中的对象直接交互。


PyImport_ImportModule

Imports the module from C as before

同上,从C语言导入模块


PyObject_GetAttrString

Fetches an object's attribute value by name

按名字获取对象的属性值


PyEval_CallObject

Calls a Python function (or class, or method)

调用Python的函数(或类,或方法)


PyArg_Parse

Converts Python objects to C values

将Python对象转换为C语言值


Py_BuildValue

Converts C values to Python objects

将C语言值转换为Python对象

We met both of the data conversion functions in Chapter 22. The PyEval_CallObject call in this version of the example is the key call here: it runs the imported function with a tuple of arguments, much like the Python apply built-in function and newer func(*args) call syntax. The Python function's return value comes back to C as a PyObject*, a generic Python object pointer.

我们在第22章 见过这两个数据转换函数。 本例中的PyEval_CallObject是关键性的调用: 它以一个元组为参数执行导入的函数, 很像Python内置函数apply, 或新的调用方法func(*args)。 Python函数返回到C语言的返回值是一个通用的Python对象指针,PyObject*

Example 23-6. PP3E/Integrate/Embed/Basics/embed-object.c

/* fetch and call objects in modules */

#include <Python.h>

main( ) {
    char *cstr;
    PyObject *pstr, *pmod, *pfunc, *pargs;
    printf("embed-object/n");
    Py_Initialize( );

    /* get usermod.message */
    pmod = PyImport_ImportModule("usermod");
    pstr = PyObject_GetAttrString(pmod, "message");

    /* convert string to C */
    PyArg_Parse(pstr, "s", &cstr);
    printf("%s/n", cstr);
    Py_DECREF(pstr);

    /* call usermod.transform(usermod.message) */
    pfunc = PyObject_GetAttrString(pmod, "transform");
    pargs = Py_BuildValue("(s)", cstr);
    pstr  = PyEval_CallObject(pfunc, pargs);
    PyArg_Parse(pstr, "s", &cstr);
    printf("%s/n", cstr);

    /* free owned objects */
    Py_DECREF(pmod);
    Py_DECREF(pstr);
    Py_DECREF(pfunc);        /* not really needed in main( ) */
    Py_DECREF(pargs);        /* since all memory goes away  */
}

When compiled and run, the result is the same again:

编译后运行的结果仍是一样的:

.../PP3E/Integrate/Embed/Basics$ embed-object
embed-object
The meaning of life...
THE MEANING OF PYTHON...

But this output is generated by C this timefirst, by fetching the Python module's message attribute value, and then by fetching and calling the module's transform function object directly and printing its return value that is sent back to C. Input to the TRansform function is a function argument here, not a preset global variable. Notice that message is fetched as a module attribute this time, instead of by running its name as a code string; there is often more than one way to accomplish the same goals with different API calls.

但是这次输出是由C语言生成的:首先,获取Python模块的message属性值, 然后获取模块的transform函数对象并直接调用, 接着打印发回到C语言的返回值。 这里输入transform函数的是一个函数参数,而不是预设的全局变量。 注意这次message是按模块属性获取的, 而不是用它的名字作为一个代码串运行; 通常有不止一个方法, 可以调用不同的API, 来达到相同的目的。

Running functions in modules like this is a simple way to structure embedding; code in the module file can be changed arbitrarily without having to recompile the C program that runs it. It also provides a direct communication model: inputs and outputs to Python code can take the form of function arguments and return values.

像这样运行模块中的函数, 是构造内嵌的一个简单的方法; 模块文件中的代码可以任意更改, 而不必重编译运行它的C程序。 它也提供了一个直接通信模式: Python代码的输入和输出, 可以采用函数参数和返回值的形式。

23.3.4. Running Strings in Dictionaries

23.3.4. 在字典中运行字符串

When we used PyRun_String earlier to run expressions with results, code was executed in the namespace of an existing Python module. However, sometimes it's more convenient to create a brand-new namespace for running code strings that is independent of any existing module files. The C file in Example 23-7 shows how; the new namespace is created as a new Python dictionary object, and a handful of new API calls are employed in the process:

前面我们使用PyRun_String, 运行表达式并返回结果时, 代码是在现存的Python模块的名字空间中执行的。 然而,当运行一个与任何现存模块文件都无关的代码串时, 创建一个全新的名字空间将更方便。 Example 23-7的C文件显示了该如何做; 创建的新的名字空间是一个新的Python字典对象, 还有,该过程中使用了几个新的API调用:


PyDict_New

Makes a new empty dictionary object

构造一个新的空字典对象


PyDict_SetItemString

Assigns to a dictionary's key

给一个字典的键值赋值


PyDict_GetItemString

Fetches (indexes) a dictionary value by key

按键值获取(查询)一个字典值


PyRun_String

Runs a code string in namespaces, as before

同上,在名字空间中运行一个代码串


PyEval_GetBuiltins

Gets the built-in scope's module

得到内置作用域的模块

The main trick here is the new dictionary. Inputs and outputs for the embedded code strings are mapped to this dictionary by passing it as the code's namespace dictionaries in the PyRun_String call. The net effect is that the C program in Example 23-7 works exactly like this Python code:

这里的关键是新建的字典。 该字典作为内嵌代码串执行的名字空间, 传递给PyRun_String调用, 代码的输入和输出就都映射到了这个字典。 实际效果是,Example 23-7 中的C程序等同与这个Python代码:

 >>> d = {}
 >>> d['Y'] = 2
 >>> exec 'X = 99' in d, d
 >>> exec 'X = X + Y' in d, d
 >>> print d['X']
 101

But here, each Python operation is replaced by a C API call.

但是这里的每一个Python操作都被替换成了C API调用。

Example 23-7. PP3E/Integrate/Embed/Basics/embed-dict.c

/***************************************************
 * make a new dictionary for code string namespace;
 ***************************************************/

#include <Python.h>

main( ) {
    int cval;
    PyObject *pdict, *pval;
    printf("embed-dict/n");
    Py_Initialize( );

    /* make a new namespace */
    pdict = PyDict_New( );
    PyDict_SetItemString(pdict, "_ _builtins_ _", PyEval_GetBuiltins( ));

    PyDict_SetItemString(pdict, "Y", PyInt_FromLong(2));   /* dict['Y'] = 2   */
    PyRun_String("X = 99",  Py_file_input, pdict, pdict);  /* run statements  */
    PyRun_String("X = X+Y", Py_file_input, pdict, pdict);  /* same X and Y    */
    pval = PyDict_GetItemString(pdict, "X");               /* fetch dict['X'] */

    PyArg_Parse(pval, "i", &cval);                         /* convert to C */
    printf("%d/n", cval);                                  /* result=101 */
    Py_DECREF(pdict);
}

When compiled and run, this C program creates this sort of output:

编译运行,该C程序输出为:

.../PP3E/Integrate/Embed/Basics$ embed-dict
embed-dict
101

The output is different this time: it reflects the value of the Python variable X assigned by the embedded Python code strings and fetched by C. In general, C can fetch module attributes either by calling PyObject_GetAttrString with the module or by using PyDict_GetItemString to index the module's attribute dictionary (expression strings work too, but they are less direct). Here, there is no module at all, so dictionary indexing is used to access the code's namespace in C.

这次输出不一样: 它反映了Python变量X的值, 该变量由内嵌代码串赋值, 并由C语言获取。 一般来说,C语言获取模块属性时, 要么通过模块调用PyObject_GetAttrString, 要么使用PyDict_GetItemString来查询模块的属性字典 (表达式字符串也行,但是不够直接)。 这里根本没有模块,所以C语言使用字典查询进入代码的名字空间。

Besides allowing you to partition code string namespaces independent of any Python module files on the underlying system, this scheme provides a natural communication mechanism. Values that are stored in the new dictionary before code is run serve as inputs, and names assigned by the embedded code can later be fetched out of the dictionary to serve as code outputs. For instance, the variable Y in the second string run refers to a name set to 2 by C; X is assigned by the Python code and fetched later by C code as the printed result.

这个方案不仅允许你隔离代码串的名字空间, 使之不依赖于任何下层系统上的Python模块文件, 而且,它还提供了一个自然的通信机制。 代码运行前保存于新建字典中的值可以作为输入, 而内嵌代码赋值的名字, 稍后可以从字典中取出,成为输出。 例如,第二个运行的字符串中的变量Y,它所指的名字在C语言中被设为2; X由Python代码赋值,后来被C代码获取,并作为结果打印。

There is one subtlety: dictionaries that serve as namespaces for running code are generally required to have a _ _builtins_ _ link to the built-in scope searched last for name lookups, set with code of this form:

有一个细微之处:运行代码时作为名字空间的字典, 一般要求具有_ _builtins_ _键值, 它将指向内置作用域,用于名字查找, 用这样的代码进行设置:

PyDict_SetItemString(pdict, "_ _builtins_ _", PyEval_GetBuiltins( ));

This is esoteric, and it is normally handled by Python internally for modules. For raw dictionaries, though, we are responsible for setting the link manually.

这有点深奥,该字典通常是由Python内部为模块自动产生的。 但是,对于原始字典,我们应负责手工设置。

23.3.5. Precompiling Strings to Bytecode

23.3.5. 预编译字符串为字节码

When you call Python function objects from C, you are actually running the already compiled bytecode associated with the object (e.g., a function body). When running strings, Python must compile the string before running it. Because compilation is a slow process, this can be a substantial overhead if you run a code string more than once. Instead, precompile the string to a bytecode object to be run later, using the API calls illustrated in Example 23-8:[*]

当你从C语言调用Python函数对象时,你实际上是运行该对象已编译的字节码(例如一个函数体)。 当运行字符串时,Python必须在运行它之前编译这个字符串。 因为编译是一个费时的过程,如果你要多次运行一个代码串,这将是可观的开销。 不过,可以使用 Example 23-8 所示的API调用,将字符串预编译为字节码对象,然后再运行: [*]

[*] In case you've forgotten: bytecode is simply an intermediate representation for already compiled program code in the current standard Python implementation. It's a low-level binary format that can be quickly interpreted by the Python runtime system. Bytecode is usually generated automatically when you import a module, but there may be no notion of an import when running raw strings from C.

[*] 可能你已经忘了:在目前的标准Python实现中, 程序代码编译后的 字节码 只是一种中间代码。 它是低级的二进制格式的,可以被Python运行时系统快速解释。 字节码通常在你导入一个模块时自动生成, 但是在C语言中运行原始字符串时,没有导入这个概念。


Py_CompileString

Compiles a string of code and returns a bytecode object

编译一个代码串并返回一个字节码对象


PyEval_EvalCode

Runs a compiled bytecode object

运行一个已编译字节码对象

The first of these takes the mode flag that is normally passed to PyRun_String, as well as a second string argument that is used only in error messages. The second takes two namespace dictionaries. These two API calls are used in Example 23-8 to compile and execute three strings of Python code in turn.

第一个函数的第二个字符串参数仅用于出错信息中, 第三个参数是一个模式标志, 与通常传递给PyRun_String的模式标志一样。 第二个函数接受两个名字空间字典。 Example 23-8 用这两个API调用依次编译与执行三个Python代码串。

Example 23-8. PP3E/Integrate/Embed/Basics/embed-bytecode.c

/* precompile code strings to bytecode objects */

#include <Python.h>
#include <compile.h>
#include <eval.h>

main( ) {
    int i;
    char *cval;
    PyObject *pcode1, *pcode2, *pcode3, *presult, *pdict;
    char *codestr1, *codestr2, *codestr3;
    printf("embed-bytecode/n");

    Py_Initialize( );
    codestr1 = "import usermod/nprint usermod.message";     /* statements */
    codestr2 = "usermod.transform(usermod.message)";        /* expression */
    codestr3 = "print '%d:%d' % (X, X ** 2),";              /* use input X */

    /* make new namespace dictionary */
    pdict = PyDict_New( );
    if (pdict == NULL) return -1;
    PyDict_SetItemString(pdict, "_ _builtins_ _", PyEval_GetBuiltins( ));

    /* precompile strings of code to bytecode objects */
    pcode1 = Py_CompileString(codestr1, "<embed>", Py_file_input);
    pcode2 = Py_CompileString(codestr2, "<embed>", Py_eval_input);
    pcode3 = Py_CompileString(codestr3, "<embed>", Py_file_input);

    /* run compiled bytecode in namespace dict */
    if (pcode1 && pcode2 && pcode3) {
        (void)    PyEval_EvalCode((PyCodeObject *)pcode1, pdict, pdict);
        presult = PyEval_EvalCode((PyCodeObject *)pcode2, pdict, pdict);
        PyArg_Parse(presult, "s", &cval);
        printf("%s/n", cval);
        Py_DECREF(presult);

        /* rerun code object repeatedly */
        for (i = 0; i <= 10; i++) {
            PyDict_SetItemString(pdict, "X", PyInt_FromLong(i));
            (void) PyEval_EvalCode((PyCodeObject *)pcode3, pdict, pdict);
        }
        printf("/n");
    }
    /* free referenced objects */
    Py_XDECREF(pdict);
    Py_XDECREF(pcode1);
    Py_XDECREF(pcode2);
    Py_XDECREF(pcode3);
}

This program combines a variety of techniques that we've already seen. The namespace in which the compiled code strings run, for instance, is a newly created dictionary (not an existing module object), and inputs for code strings are passed as preset variables in the namespace. When built and executed, the first part of the output is similar to previous examples in this section, but the last line represents running the same precompiled code string 11 times:

这个程序组合了我们所见过的多种技术。 例如,编译的代码串所运行的名字空间是新建的字典(而不是已存在的模块对象), 代码串的输入是通过名字空间中的预设变量传递的。 编译执行时,输出的第一部分与本节以前的例子类似, 但最后一行是相同的预编译代码串运行11次的结果:

.../PP3E/Integrate/Embed/Basics$ embed-bytecode
embed-bytecode
The meaning of life...
THE MEANING OF PYTHON...
0:0 1:1 2:4 3:9 4:16 5:25 6:36 7:49 8:64 9:81 10:100

If your system executes strings multiple times, it is a major speedup to precompile to bytecode in this fashion.

如果你的系统需要多次执行字符串,以这种方式预编译成字节码可以大大提高速度。



(转载请注明来源于金庆的专栏)
0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:1074647次
    • 积分:15230
    • 等级:
    • 排名:第761名
    • 原创:377篇
    • 转载:49篇
    • 译文:19篇
    • 评论:394条
    文章分类
    文章存档
    最新评论