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.
在前面的概覽一節(jié)中你已經(jīng)知道,內(nèi)嵌領(lǐng)域的靈活性很大。本節(jié)列舉了幾個(gè)簡(jiǎn)短的C程序,它們以一種形式或另一種形式運(yùn)行Python代碼,來(lái)演示實(shí)際的通用的內(nèi)嵌技術(shù)。多數(shù)例子使用了這個(gè)簡(jiǎn)單的Python模塊,見(jiàn)Example 23-1。(頭部注釋為:“C代碼以內(nèi)嵌模式運(yùn)行本模塊中的Python代碼。這樣一個(gè)文件可以更改而不必更改C代碼層。這只是標(biāo)準(zhǔn)的Python代碼(C代碼做轉(zhuǎn)換工作)。你也可以運(yùn)行標(biāo)準(zhǔn)模塊如string中的代碼。”)
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:
如果你知道一點(diǎn)Python,你可能知道,這個(gè)文件定義了一個(gè)字符串與一個(gè)函數(shù);函數(shù)將輸入串替換并轉(zhuǎn)換成大寫(xiě)后返回。這在Python中很容易使用:
With a little Python API wizardry, it's not much more difficult to use this module the same way in C.
利用一點(diǎn)Python API的魔力,在C語(yǔ)言中以同樣的方式使用這個(gè)模塊也不是很難。
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語(yǔ)言運(yùn)行Python代碼,最簡(jiǎn)單的方法可能是,調(diào)用PyRun_SimpleString API 函數(shù)。使用它,C程序可以執(zhí)行用C字符串?dāng)?shù)組表示的Python程序。這個(gè)調(diào)用的限制也很大:所有化碼運(yùn)行于同一命名空間(模塊_ _main_ _),代碼串必須是Python語(yǔ)句(不是表達(dá)式),也沒(méi)有直接的辦法可以與運(yùn)行的Python代碼通信,進(jìn)行輸入與輸出。
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.
但它仍是一個(gè)好的開(kāi)始。另外,如果在參數(shù)中導(dǎo)入C擴(kuò)展模塊,通過(guò)該擴(kuò)展模塊,內(nèi)嵌的Python代碼就可以與外面的C語(yǔ)言層通信了,這一技術(shù)可以滿足許多內(nèi)嵌目標(biāo)。C程序Example 23-2 演示了這一基本方法,它運(yùn)行Python代碼,產(chǎn)生與上面交互式運(yùn)行相同的結(jié)果。
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.
首先請(qǐng)注意,當(dāng)Python內(nèi)嵌時(shí),在調(diào)用任何其它API函數(shù)之前,C程序總是先調(diào)用Py_Initialize,來(lái)初始化鏈接的Python庫(kù)。代碼余下部分與交互方式下打字輸入差不多,直截了當(dāng)?shù)叵騊ython提交硬編碼的字符串。在內(nèi)部,PyRun_SimpleString調(diào)用Python編譯器和解釋器,來(lái)運(yùn)行C語(yǔ)言傳入的字符串;通常,在安裝了Python的系統(tǒng)上,Python編譯器總是可用的。
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.
為了從這個(gè)C語(yǔ)言源文件構(gòu)建獨(dú)立的執(zhí)行程序,編譯后需要鏈接Python庫(kù)文件。在本章中,“庫(kù)”通常指Python編譯后生成的二進(jìn)制庫(kù)文件,而不是Python源代碼庫(kù)。
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.
目前,當(dāng)Python解釋器構(gòu)建時(shí),你在C語(yǔ)言中所需的所有Python的東西,會(huì)被編譯到一個(gè)Python庫(kù)文件(在Cygwin上即libpython2.4.dll)。程序的main函數(shù)來(lái)自于你的C語(yǔ)言代碼,與你的平臺(tái)和Python安裝的擴(kuò)展有關(guān),你可能還需要鏈接Python庫(kù)所引用的外部庫(kù)。
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.
假設(shè)不需要額外的擴(kuò)展庫(kù),Example 23-3是在Windows的Cygwin下構(gòu)建Example 23-2C程序的最小的make文件。同樣,make文件的細(xì)節(jié)隨平臺(tái)而變,請(qǐng)查看Python手冊(cè)獲取幫助。這個(gè)make文件在編譯時(shí)使用Python包含路徑來(lái)尋找Python.h,并在最后的鏈接步驟中添加Python庫(kù)文件,使API調(diào)用在C程序中可用。
To build a program with this file, launch make on it as usual:
用這個(gè)文件構(gòu)建程序時(shí),按通常方式運(yùn)行make:
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.
但是,實(shí)際可能不這么容易,會(huì)需要一點(diǎn)技術(shù)。Example 23-4是我在Cygwin下實(shí)際使用的make文件,用于構(gòu)建本章所有的C程序。
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.
在有些平臺(tái)上,你可能需要鏈接其它庫(kù),因?yàn)橛玫降腜ython庫(kù)文件依賴(lài)和要求外部庫(kù)。實(shí)際上,你的Python庫(kù)可能要求你必須鏈接許多外部庫(kù),并且坦白說(shuō),找出所有的鏈接依賴(lài)關(guān)系是很煩的。要求的庫(kù)隨平臺(tái)和安裝的Python不同而有很大不同,所以我也沒(méi)辦法使這個(gè)過(guò)程簡(jiǎn)單點(diǎn)(畢竟這是C語(yǔ)言)。請(qǐng)運(yùn)用標(biāo)準(zhǔn)的C語(yǔ)言開(kāi)發(fā)技術(shù)來(lái)解決。
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.
注意,在有些平臺(tái)上,如果你要做許多內(nèi)嵌工作,并且你碰到了外部依賴(lài)問(wèn)題,你可能需要在你的機(jī)器上,從源碼構(gòu)建Python,并在Modules/Setup文件(或在新版本中的頂層setup.py Distutils腳本)中,禁用所有不必要的擴(kuò)展。這樣生成的Python庫(kù),具有最小的外部依賴(lài),鏈接時(shí)會(huì)方便許多。
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.
例如,如果你的內(nèi)嵌代碼不會(huì)構(gòu)建GUI,就可以直接從庫(kù)里面移除Tkinter;詳情請(qǐng)參閱Python源代碼樹(shù)中頂層的README文件。在Python源代碼樹(shù)中,在生成的make文件中,你也可以找到你的Python引用外部庫(kù)的列表。無(wú)論如何,好消息是鏈接依賴(lài)問(wèn)題你只需解決一次。
Once you've gotten the makefile to work, run it to build the C program with Python libraries linked in:
一旦make文件可以工作,運(yùn)行它來(lái)構(gòu)建C程序,并鏈接Python庫(kù):
After building, run the resulting C program as usual, regardless of how this works in your platform:[*]
構(gòu)建之后,運(yùn)行生成的C程序(不同平臺(tái)方法不同[*]):
[*] 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下,為了運(yùn)行內(nèi)嵌的例程,我必須先使用shell命令export PYTHONPATH=.,來(lái)設(shè)置PYTHONPATH包含當(dāng)前目錄。同時(shí),由于我的系統(tǒng)路徑設(shè)置關(guān)系,我必須使用shell命令./embed-simple來(lái)執(zhí)行程序。你的方法可以不同;如果你有麻煩,試試在C語(yǔ)言中運(yùn)行內(nèi)嵌Python命令import sys和print sys.path,看看Python路徑是怎樣的,更多內(nèi)嵌應(yīng)用的路徑配置信息,請(qǐng)查閱Python/C API手冊(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下,為了運(yùn)行內(nèi)嵌的例程,我必須先使用shell命令export PYTHONPATH=.,來(lái)設(shè)置PYTHONPATH包含當(dāng)前目錄。同時(shí),由于我的系統(tǒng)路徑設(shè)置關(guān)系,我必須使用shell命令./embed-simple來(lái)執(zhí)行程序。你的方法可以不同;如果你有麻煩,試試在C語(yǔ)言中運(yùn)行內(nèi)嵌Python命令import sys和print sys.path,看看Python路徑是怎樣的,更多內(nèi)嵌應(yīng)用的路徑配置信息,請(qǐng)查閱Python/C API手冊(cè)。
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.
大多數(shù)的輸出是由Python print語(yǔ)句產(chǎn)生的,該語(yǔ)句由C語(yǔ)言發(fā)送到鏈入的Python庫(kù)。就像C成為了一個(gè)交互式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.
當(dāng)然,C語(yǔ)言運(yùn)行的Python代碼串,可能不是這樣在C語(yǔ)言文件中硬編碼的。它們可以是從一個(gè)文本文件或GUI讀取、從HTML或XML文件中提取、從數(shù)據(jù)庫(kù)或socket獲取、等等。使用這樣的外部源,就可以任意更改C語(yǔ)言運(yùn)行的Python代碼串,而不必重新編譯運(yùn)行它們的C程序。它們甚至可以由系統(tǒng)的最終用戶現(xiàn)場(chǎng)更改。但是,為充分利用代碼串,我們需要繼續(xù)前進(jìn),學(xué)習(xí)更靈活的API工具。
Example 23-5 uses the following API calls to run code strings that return expression results back to C:
Example 23-5使用下面的API調(diào)用來(lái)運(yùn)行代碼串,并返回表達(dá)式的結(jié)果到C:
Py_Initialize
Initializes linked-in Python libraries as before
同上,初始化鏈入的Python庫(kù)
PyImport_ImportModule
Imports a Python module and returns a pointer to it
導(dǎo)入一個(gè)Python模塊并返回它的指針
PyModule_GetDict
Fetches a module's attribute dictionary object
獲取一個(gè)模塊的屬性字典對(duì)象
PyRun_String
Runs a string of code in explicit namespaces
在指定的名字空間內(nèi)運(yùn)行代碼串
PyObject_SetAttrString
Assigns an object attribute by namestring
通過(guò)namestring為一個(gè)對(duì)象的屬性賦值
PyArg_Parse
Converts a Python return value object to C form
將Python返回值對(duì)象轉(zhuǎn)換為C語(yǔ)言形式
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.
導(dǎo)入調(diào)用用來(lái)獲取前面Example 23-1中所列模塊usermod的名字空間,然后,代碼串就可以直接在那里運(yùn)行(并且可以訪問(wèn)那個(gè)模塊內(nèi)定義的名字,無(wú)需修飾)。PyImport_ImportModule就像Python的import語(yǔ)句,但是導(dǎo)入的模塊對(duì)象會(huì)返回給C語(yǔ)言;它并不賦值給一個(gè)Python變量名。結(jié)果是,它可能更像Python的_ _import_ _內(nèi)置函數(shù)。
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調(diào)用是實(shí)際運(yùn)行代碼的地方。它接受一個(gè)代碼串,一個(gè)分析模式標(biāo)志,和兩個(gè)字典對(duì)象指針,這兩個(gè)字典分別作為代碼串運(yùn)行的全局和局部的名字空間。模式標(biāo)志可以是Py_eval_input,來(lái)運(yùn)行一個(gè)表達(dá)式,或者Py_file_input,來(lái)運(yùn)行一個(gè)語(yǔ)句;當(dāng)運(yùn)行表達(dá)式時(shí),這個(gè)調(diào)用會(huì)返回表達(dá)式的計(jì)算結(jié)果(返回一個(gè)對(duì)象指針PyObject*)。兩個(gè)名字空間字典指針參數(shù)允許你區(qū)分全局和局部作用域,但是它們一般是同一個(gè)字典,如此代碼會(huì)運(yùn)行于單一的名字空間。[*]
(譯注:難道就不能不分表達(dá)式和語(yǔ)句嗎?)
[*] 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. [*]有一個(gè)相關(guān)的函數(shù)可以讓你運(yùn)行代碼文件,但是本章沒(méi)有示例:PyObject* PyRun_File(FILE *fp, char *filename, mode, globals, locals)。因?yàn)槟憧偸强梢宰x取文件內(nèi)容,并作為一個(gè)代碼串,用PyRun_String來(lái)運(yùn)行它,所以PyRun_File并不總是必要的。在多行代碼串的情況下,可用\n字符分隔行,并照常縮進(jìn)代碼塊。
[*] 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.
[*]有一個(gè)相關(guān)的函數(shù)可以讓你運(yùn)行代碼文件,但是本章沒(méi)有示例:PyObject* PyRun_File(FILE *fp, char *filename, mode, globals, locals)。因?yàn)槟憧偸强梢宰x取文件內(nèi)容,并作為一個(gè)代碼串,用PyRun_String來(lái)運(yùn)行它,所以PyRun_File并不總是必要的。在多行代碼串的情況下,可用\n字符分隔行,并照常縮進(jìn)代碼塊。
When compiled and run, this file produces the same result as its predecessor:
編譯后運(yùn)行的結(jié)果與上個(gè)程序是一樣的:
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.
但是為了產(chǎn)生這個(gè)輸出,所做的工作是很不一樣的。這一次,通過(guò)運(yùn)行一個(gè)字符串表達(dá)式,C語(yǔ)言直接獲取,轉(zhuǎn)換并打印Python模塊的message屬性,并在模塊的名字空間中,對(duì)一個(gè)全局變量(X)賦值,該變量再作為Python print語(yǔ)句的輸入。
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.
因?yàn)樵摪姹镜淖址\(yùn)行函數(shù)可以讓你指定名字空間,你可以更好地隔離系統(tǒng)中運(yùn)行的內(nèi)嵌代碼,每個(gè)代碼組都可以有一個(gè)獨(dú)特的名字空間,避免改寫(xiě)其它組的變量。并且,這個(gè)函數(shù)返回一個(gè)結(jié)果,讓你可以更好地與內(nèi)嵌代碼通信;表達(dá)式結(jié)果是輸出,在代碼運(yùn)行的名字空間內(nèi),它又賦值給全局變量,而全局變量可以作為輸入。
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.
在我們繼續(xù)前進(jìn)前,我要解釋一下,這里有兩個(gè)代碼問(wèn)題。首先,這個(gè)程序使用第22章介紹的Py_DECREF函數(shù),減小了從Python傳來(lái)的對(duì)象的引用計(jì)數(shù)。這些調(diào)用在此并非嚴(yán)格需要(無(wú)論如何,對(duì)象占用空間在程序退出時(shí)會(huì)收回),但是它們演示了,當(dāng)Python將對(duì)象所有權(quán)傳遞到C語(yǔ)言時(shí),內(nèi)嵌接口必須如何管理引用計(jì)數(shù)。如果這是個(gè)大型系統(tǒng)中的函數(shù)調(diào)用,通常你需要減小計(jì)數(shù),以允許Python收回對(duì)象。
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.
第二,在一個(gè)實(shí)際的程序中,你一般應(yīng)該立即檢查程序中所有API調(diào)用的返回值,以檢測(cè)錯(cuò)誤(例如導(dǎo)入失敗)。本節(jié)中的例子省略了錯(cuò)誤檢查,以保持代碼清晰,但是它們將會(huì)在以后的代碼清單中出現(xiàn),并且應(yīng)該包含在你的程序中,使之更健壯。
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:
上面兩節(jié)討論的是代碼串的運(yùn)行,但是對(duì)C程序來(lái)說(shuō),按Python對(duì)象運(yùn)行也很容易。Example 23-6實(shí)現(xiàn)Examples 23-2和23-5相同的任務(wù),但是使用其它的API工具,來(lái)與Python模塊中的對(duì)象直接交互。
Imports the module from C as before
同上,從C語(yǔ)言導(dǎo)入模塊
PyObject_GetAttrString
Fetches an object's attribute value by name
按名字獲取對(duì)象的屬性值
PyEval_CallObject
Calls a Python function (or class, or method)
調(diào)用Python的函數(shù)(或類(lèi),或方法)
Converts Python objects to C values
將Python對(duì)象轉(zhuǎn)換為C語(yǔ)言值
Py_BuildValue
Converts C values to Python objects
將C語(yǔ)言值轉(zhuǎn)換為Python對(duì)象
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.
我們?cè)?a class="docLink" href="http://www.shnenglu.com/jinq0123/admin/python3-CHP-22.html#python3-CHP-22">第22章見(jiàn)過(guò)這兩個(gè)數(shù)據(jù)轉(zhuǎn)換函數(shù)。本例中的PyEval_CallObject是關(guān)鍵性的調(diào)用:它以一個(gè)元組為參數(shù)執(zhí)行導(dǎo)入的函數(shù),很像Python內(nèi)置函數(shù)apply,或新的調(diào)用方法func(*args)。Python函數(shù)返回到C語(yǔ)言的返回值是一個(gè)通用的Python對(duì)象指針,PyObject*。
When compiled and run, the result is the same again:
編譯后運(yùn)行的結(jié)果仍是一樣的:
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語(yǔ)言生成的:首先,獲取Python模塊的message屬性值,然后獲取模塊的transform函數(shù)對(duì)象并直接調(diào)用,接著打印發(fā)回到C語(yǔ)言的返回值。這里輸入transform函數(shù)的是一個(gè)函數(shù)參數(shù),而不是預(yù)設(shè)的全局變量。注意這次message是按模塊屬性獲取的,而不是用它的名字作為一個(gè)代碼串運(yùn)行;通常有不止一個(gè)方法,可以調(diào)用不同的API,來(lái)達(dá)到相同的目的。
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.
像這樣運(yùn)行模塊中的函數(shù),是構(gòu)造內(nèi)嵌的一個(gè)簡(jiǎn)單的方法;模塊文件中的代碼可以任意更改,而不必重編譯運(yùn)行它的C程序。它也提供了一個(gè)直接通信模式:Python代碼的輸入和輸出,可以采用函數(shù)參數(shù)和返回值的形式。
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,運(yùn)行表達(dá)式并返回結(jié)果時(shí),代碼是在現(xiàn)存的Python模塊的名字空間中執(zhí)行的。然而,當(dāng)運(yùn)行一個(gè)與任何現(xiàn)存模塊文件都無(wú)關(guān)的代碼串時(shí),創(chuàng)建一個(gè)全新的名字空間將更方便。Example 23-7的C文件顯示了該如何做;創(chuàng)建的新的名字空間是一個(gè)新的Python字典對(duì)象,還有,該過(guò)程中使用了幾個(gè)新的API調(diào)用:
PyDict_New
Makes a new empty dictionary object
構(gòu)造一個(gè)新的空字典對(duì)象
PyDict_SetItemString
Assigns to a dictionary's key
給一個(gè)字典的鍵值賦值
PyDict_GetItemString
Fetches (indexes) a dictionary value by key
按鍵值獲取(查詢)一個(gè)字典值
Runs a code string in namespaces, as before
同上,在名字空間中運(yùn)行一個(gè)代碼串
PyEval_GetBuiltins
Gets the built-in scope's module
得到內(nèi)置作用域的模塊
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:
這里的關(guān)鍵是新建的字典。該字典作為內(nèi)嵌代碼串執(zhí)行的名字空間,傳遞給PyRun_String調(diào)用,代碼的輸入和輸出就都映射到了這個(gè)字典。實(shí)際效果是,Example 23-7中的C程序等同與這個(gè)Python代碼:
But here, each Python operation is replaced by a C API call.
但是這里的每一個(gè)Python操作都被替換成了C API調(diào)用。
When compiled and run, this C program creates this sort of output:
編譯運(yùn)行,該C程序輸出為:
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的值,該變量由內(nèi)嵌代碼串賦值,并由C語(yǔ)言獲取。一般來(lái)說(shuō),C語(yǔ)言獲取模塊屬性時(shí),要么通過(guò)模塊調(diào)用PyObject_GetAttrString,要么使用PyDict_GetItemString來(lái)查詢模塊的屬性字典(表達(dá)式字符串也行,但是不夠直接)。這里根本沒(méi)有模塊,所以C語(yǔ)言使用字典查詢進(jìn)入代碼的名字空間。
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.
這個(gè)方案不僅允許你隔離代碼串的名字空間,使之不依賴(lài)于任何下層系統(tǒng)上的Python模塊文件,而且,它還提供了一個(gè)自然的通信機(jī)制。代碼運(yùn)行前保存于新建字典中的值可以作為輸入,而內(nèi)嵌代碼賦值的名字,稍后可以從字典中取出,成為輸出。例如,第二個(gè)運(yùn)行的字符串中的變量Y,它所指的名字在C語(yǔ)言中被設(shè)為2;X由Python代碼賦值,后來(lái)被C代碼獲取,并作為結(jié)果打印。
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:
有一個(gè)細(xì)微之處:運(yùn)行代碼時(shí)作為名字空間的字典,一般要求具有_ _builtins_ _鍵值,它將指向內(nèi)置作用域,用于名字查找,用這樣的代碼進(jìn)行設(shè)置:
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.
這有點(diǎn)深?yuàn)W,該字典通常是由Python內(nèi)部為模塊自動(dòng)產(chǎn)生的。但是,對(duì)于原始字典,我們應(yīng)負(fù)責(zé)手工設(shè)置。
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:[*]
當(dāng)你從C語(yǔ)言調(diào)用Python函數(shù)對(duì)象時(shí),你實(shí)際上是運(yùn)行該對(duì)象已編譯的字節(jié)碼(例如一個(gè)函數(shù)體)。當(dāng)運(yùn)行字符串時(shí),Python必須在運(yùn)行它之前編譯這個(gè)字符串。因?yàn)榫幾g是一個(gè)費(fèi)時(shí)的過(guò)程,如果你要多次運(yùn)行一個(gè)代碼串,這將是可觀的開(kāi)銷(xiāo)。不過(guò),可以使用Example 23-8所示的API調(diào)用,將字符串預(yù)編譯為字節(jié)碼對(duì)象,然后再運(yùn)行:[*]
[*] 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. [*]可能你已經(jīng)忘了:在目前的標(biāo)準(zhǔn)Python實(shí)現(xiàn)中,程序代碼編譯后的字節(jié)碼只是一種中間代碼。它是低級(jí)的二進(jìn)制格式的,可以被Python運(yùn)行時(shí)系統(tǒng)快速解釋。字節(jié)碼通常在你導(dǎo)入一個(gè)模塊時(shí)自動(dòng)生成,但是在C語(yǔ)言中運(yùn)行原始字符串時(shí),沒(méi)有導(dǎo)入這個(gè)概念。
[*] 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.
[*]可能你已經(jīng)忘了:在目前的標(biāo)準(zhǔn)Python實(shí)現(xiàn)中,程序代碼編譯后的字節(jié)碼只是一種中間代碼。它是低級(jí)的二進(jìn)制格式的,可以被Python運(yùn)行時(shí)系統(tǒng)快速解釋。字節(jié)碼通常在你導(dǎo)入一個(gè)模塊時(shí)自動(dòng)生成,但是在C語(yǔ)言中運(yùn)行原始字符串時(shí),沒(méi)有導(dǎo)入這個(gè)概念。
Py_CompileString
Compiles a string of code and returns a bytecode object
編譯一個(gè)代碼串并返回一個(gè)字節(jié)碼對(duì)象
PyEval_EvalCode
Runs a compiled bytecode object
運(yùn)行一個(gè)已編譯字節(jié)碼對(duì)象
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.
第一個(gè)函數(shù)的第二個(gè)字符串參數(shù)僅用于出錯(cuò)信息中,第三個(gè)參數(shù)是一個(gè)模式標(biāo)志,與通常傳遞給PyRun_String的模式標(biāo)志一樣。第二個(gè)函數(shù)接受兩個(gè)名字空間字典。Example 23-8用這兩個(gè)API調(diào)用依次編譯與執(zhí)行三個(gè)Python代碼串。
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:
這個(gè)程序組合了我們所見(jiàn)過(guò)的多種技術(shù)。例如,編譯的代碼串所運(yùn)行的名字空間是新建的字典(而不是已存在的模塊對(duì)象),代碼串的輸入是通過(guò)名字空間中的預(yù)設(shè)變量傳遞的。編譯執(zhí)行時(shí),輸出的第一部分與本節(jié)以前的例子類(lèi)似,但最后一行是相同的預(yù)編譯代碼串運(yùn)行11次的結(jié)果:
If your system executes strings multiple times, it is a major speedup to precompile to bytecode in this fashion.
如果你的系統(tǒng)需要多次執(zhí)行字符串,以這種方式預(yù)編譯成字節(jié)碼可以大大提高速度。
Powered by: C++博客 Copyright © 金慶