::-- JinQing [2006-11-08 03:31:39]

22.4. A Simple C Extension Module

22.4. 一个简单的C扩展模块

At least that's the short story; we need to turn to some code to make this more concrete. C types generally export a C module with a constructor function. Because of that, and because they are simpler, let's start off by studying the basics of C module coding with a quick example.

简短的概览之后,我们需要用代码来更具体地展示C扩展。C类型一般导出一个C模块,其中有一个构造函数。那样很简单,因此,让我们用一个简例从编写C模块的基础开始学习。

As mentioned, when you add new or existing C components to Python, you need to code an interface ("glue") logic layer in C that handles cross-language dispatching and data translation. The C source file in Example 22-1 shows how to code one by hand. It implements a simple C extension module named hello for use in Python scripts, with a function named message that simply returns its input string argument with extra text prepended. Python scripts will call this function as usual, but this one is coded in C, not in Python.

如前所述,当你向Python添加新的或已有的C部件,你需要用C编写接口逻辑层(粘合层),来处理语言间的分派和数据翻译。例22-1的C语言源文件显示了如何对此进行手工编码。它实现了一个简单的C扩展模块hello,有一个函数message,简单地对输入串添加一些文本并返回,它可在Python脚本中使用。Python脚本像通常那样调用这个函数,但这个函数是用C写的,而不是Python.

Example 22-1. PP3E\Integrate\Extend\Hello\hello.c

/********************************************************************
 * A simple C extension module for Python, called "hello"; compile
 * this into a ".so" on python path, import and call hello.message;
 ********************************************************************/

#include <Python.h>
#include <string.h>

/* module functions */
static PyObject *                                 /* returns object */
message(PyObject *self, PyObject *args)           /* self unused in modules */
{                                                 /* args from Python call */
    char *fromPython, result[64];
    if (! PyArg_Parse(args, "(s)", &fromPython))  /* convert Python -> C */
        return NULL;                              /* null=raise exception */
    else {
        strcpy(result, "Hello, ");                /* build up C string */
        strcat(result, fromPython);               /* add passed Python string */
        return Py_BuildValue("s", result);        /* convert C -> Python */
    }
}

/* registration table  */
static struct PyMethodDef hello_methods[] = {
    {"message", message, 1},       /* method name, C func ptr, always-tuple */
    {NULL, NULL}                   /* end of table marker */
};

/* module initializer */
void inithello( )                       /* called on first import */
{                                      /* name matters if loaded dynamically */
    (void) Py_InitModule("hello", hello_methods);   /* mod name, table ptr */
}


Ultimately, Python code will call this C file's message function, passing in a string object and getting back a new string object. First, though, it has to be somehow linked into the Python interpreter. To use this C file in a Python script, compile it into a dynamically loadable object file (e.g., hello.so on Linux, hello.dll under Cygwin on Windows) with a makefile like the one listed in Example 22-2, and drop the resulting object file into a directory listed on your module import search path exactly as though it were a .py or .pyc file.

最终Python代码将调用这个C文件中的message函数,传入一个字符串对象并返回一个新的字符串对象。但是,它首先必须以某种方式连接到Python解释器。为了在Python脚本中使用这个C文件,可使用类似例22-2所列的make文件,将它编译成动态装载的目标文件(例如,Linux上的hello.so,Windows上的Cygwin下的hello.dll),并将结果目标文件放入你的导入模块搜索路径,就像它是一个.py或.pyc文件。

Example 22-2. PP3E\Integrate\Extend\Hello\makefile.hello

#############################################################
# Compile hello.c into a shareable object file on Cygwin,
# to be loaded dynamically when first imported by Python.
#############################################################

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

hello.dll: hello.c
        gcc hello.c -g -I$(PYINC) -shared -L$(PYLIB) -lpython2.4 -o hello.dll

clean:
        rm -f hello.dll core


This is a Cygwin makefile that uses gcc to compile our C code; other platforms are analogous but will vary. As mentioned in Chapter 5 in the sidebar "Forking on Windows with Cygwin," Cygwin provides a Unix-like environment and libraries on Windows. To work along with the examples here, either see http://www.cygwin.com for download details or change the makefiles listed per your compiler and platform requirements. Be sure to include the path to Python's install directory with -I flags to access Python include (a.k.a. header) files, as well as the path to the Python binary library file with -L flags, if needed.

这是一个Cygwin make文件,用gcc作编译器;在其它平台会有不同,但是类似。在第5章的补充栏“在Windows上用Cygwin来分支进程”中提过,Cygwin在Windows上提供了一个类Unix的环境和库。要让这里的例子工作,要么查看http://www.cygwin.com上的下载详情,要么按你的平台和编译器的要求更改make文件。注意要用-I参数包含Python的安装路径,这样才能找到Python包含文件,也叫头文件,必要时还要用-L参数指出Python的二进制库文件的路径。

Now, to use the makefile in Example 22-2 to build the extension module in Example 22-1, simply type a standard make command at your shell (the Cygwin shell is used here):

现在,在你的shell界面简单地敲入标准make命令,用例22-2的make文件来构建例22-1的扩展模块,本例中使用的是Cygwin shell:

.../PP3E/Integrate/Extend/Hello$ make -f makefile.hello
gcc hello.c -g -I/usr/include/python2.4 -shared
                   -L/usr/bin -lpython2.4 -o hello.dll


This generates a shareable object filea .dll under Cygwin on Windows. When compiled this way, Python automatically loads and links the C module when it is first imported by a Python script. At import time, the .dll binary library file will be located in a directory on the Python import search path, just like a .py file. Because Python always searches the current working directory on imports, this chapter's examples will run from the directory you compile them in (.) without any file copies or moves. In larger systems, you will generally place compiled extensions in a directory listed in PYTHONPATH or .pth files instead.

这样会生成一个共享目标文件,在Windows的Cygwin上是一个.dll文件。这样编译后,Python会在脚本第一次导入时,自动装载和链接这个C模块。当导入时,将会在Python的导入搜索路径中查找该.dll二进制库文件,就像一个.py文件。因为Python导入时总是搜索当前工作目录,本章的例子将不进行复制或移动,而是在你编译的当前目录(.)下运行。而在大型系统中,你一般会将编译的扩展模块放在PYTHONPATH或.pth文件所列目录下。

Finally, to call the C function from a Python program, simply import the module hello and call its hello.message function with a string; you'll get back a normal Python string:

最后,在Python程序中调用C函数,只需导入hello模块并调用hello.message函数;你将得到一个Python串返回值。

.../PP3E/Integrate/Extend/Hello$ python
>>> import hello                                   # import a C module
>>> hello.message('world')                         # call a C function
'Hello, world'
>>> hello.message('extending')
'Hello, extending'


And that's ityou've just called an integrated C module's function from Python. The most important thing to notice here is that the C function looks exactly as if it were coded in Python. Python callers send and receive normal string objects from the call; the Python interpreter handles routing calls to the C function, and the C function itself handles Python/C data conversion chores.

这就成了,你刚才已经在Python中调用了一个集成的C模块的函数。着重要注意的是,这里C函数看起来完全像是用Python编写的。Python调用者传入和接收了一个普通的字符串对象;Python解译器将调用发送给C函数,C函数本身处理了Python/C的数据转换。

In fact, there is little to distinguish hello as a C extension module at all, apart from its filename. Python code imports the module and fetches its attributes as if it had been written in Python. C extension modules even respond to dir calls as usual and have the standard module and filename attributes (though the filename doesn't end in a .py or .pyc this time around):

实际上,除了文件名之外,很难分辨出hello是一个C扩展模块。Python代码导入模块并获取它的属性,就像它是Python写的一样。C扩展模块甚至像通常那样响应dir调用,并有标准的模块和文件名属性(尽管这里的文件名不是.py或.pyc结尾)。

>>> dir(hello)                                     # C module attributes
['_ _doc_ _', '_ _file_ _', '_ _name_ _', 'message']

>>> hello._ _name_ _, hello._ _file_ _
('hello', 'hello.dll')

>>> hello.message                                  # a C function object
<built-in function message>
>>> hello                                          # a C module object
<module 'hello' from 'hello.dll'>


Like any module in Python, you can also access the C extension from a script file. The Python file in Example 22-3, for instance, imports and uses the C extension module.

你也可以在脚本文件中像Python模块一样使用C扩展模块。例22-3的Python文件导入并使用了C扩展模块。

Example 22-3. PP3E\Integrate\Extend\Hello\hellouse.py

   1 import hello
   2 
   3 print hello.message('C')
   4 print hello.message('module ' + hello._ _file_ _)
   5 
   6 for i in range(3):
   7     print hello.message(str(i))


Run this script as any otherwhen the script first imports the module hello, Python automatically finds the C module's .dll object file in a directory on the module search path and links it into the process dynamically. All of this script's output represents strings returned from the C function in the file hello.c:

像其它脚本那样运行这个脚本。当第一次导入模块hello时,Python自动在模块搜索路径中找到C模块的.dll目标文件,并将它动态链接到进程内。脚本的输出就是hello.c文件的C函数的返回串。

.../PP3E/Integrate/Extend/Hello$ python hellouse.py
Hello, C
Hello, module /cygdrive/c/.../PP3E/Integrate/Extend/Hello/hello.dll
Hello, 0
Hello, 1
Hello, 2