1.4 模块方法表和初始化函数
下面,我演示如何从Python程序调用spam_system().首先,我们需要在’方法表’列出名称和地址:
static PyMethodDef SpamMethods[] = { ... {"system", spam_system, METH_VARARGS, "Execute a shell command."}, ... {NULL, NULL, 0, NULL} /* Sentinel */};
注意第三项("METH_VARARGS")。这是一个标志,用来告诉解释器应使用调用C函数的规则。它一般总是 "METH_VARARGS" 或 "METH_VARARGS | METH_KEYWORDS"; 值0表示PyArg_ParseTuple()函数使用的变量是废弃的。
当仅使用时"METH_VARARGS",函数希望Python-level参数经由PyArg_ParseTuple()被传递进来。下面提供这个函数的详细信息。
如果关键字参数被传递到函数,第三个字段的METH_KEYWORDS应被设置为1。在这中情况下, C函数应接受第三个 "PyObject *"参数,此参数是关键字字典。使用PyArg_ParseTupleAndKeywords()函数分析参数。方法表必须在模块初始化函数中传递到解释器。初始化函数必须命名为initname(), 名称是模块名称,并且在模块文件的定义是non-static的。
PyMODINIT_FUNCinitspam(void){ (void) Py_InitModule("spam", SpamMethods);}
注意: PyMODINIT_FUNC声明函数返回值类型为void,并声明平台所需的特定连接指示,对于C++声明函数为extern "C"类型。
当Python程序第一次导入spam模块时,initspam()函数被调用 (参见下面关于嵌入Python的说明)。 initspam()函数调用Py_InitModule(),Py_InitModule()函数创建模块对象 (插入到sys.modules字典"spam"键下),并在根据第二个传入参数的表(PyMethodDef结构数组)所创建的对象的基础上,插入内置功能对象。 Py_InitModule()函数返回一个指向它所创建对象的指针(此处没有使用)。如果模块不能正确初始化,函数则因严重错误终止,所以,调用者不必检查错误。
当嵌入Python时,除非在_PyImport_Inittab表中有一项,否则initspam()不被自动调用。最早的处理此事的办法是:在调用Py_Initialize()后,直接调用你的静态初始化的静态连接模块的initspam()
int
main(int argc, char *argv[])
{
/* Pass argv[0] to the Python interpreter */
Py_SetProgramName(argv[0]);
/* Initialize the Python interpreter. Required. */
Py_Initialize();
/* Add a static module */
initspam();
}
在Python源代码包,你能找到一个例子:Demo/embed/demo.c。
注意: 对于一些扩展模块,从sys.modules 模块删除入口点,或在一个进程(或在没有exec()干预的fork()后)中的多个解释器中导入编译后的模块,会产生一些问题。当初始化内部数据结构时,模块作者应该小心处理。还要注意扩展模块能够使用reload()函数,并且调用模块的初始化函数(在本例中是initspam()函数),但如果模块是从可动态加载文件(.so on Unix, .dll on Windows)中加载的,则不会再次加载模块。
Python源代码发布包中包括更多模块例子,一般在Modules/xxmodule.c。这些文件可以用做模板或简单阅读的例子。源代码发布中包括modulator.py脚本或windows安装提供一个简单的图形界面接口声明函数和要实现的模块对象,并产生一个可以填充的模板。脚本位于Tools/modulator/路径,详细信息请参阅README文件。
1.5 编译和连接
在你使用扩展之前,有两件事情需要做:编译和与Python系统连接。如果你使用动态加载,细节依赖于你的系统用户的动态加载风格。关于详细信息,构造扩展模块参见(chapter 3),至于仅在WINDOWS上构造的其他相关信息,参见(chapter 4).
如果你不使用动态加载,或者说,你想使你的模块永久成为Python解释器的一部分,必须改变安装设置,并重新编译解释器,这在Unix上是非常简单的,只需要放置你的文件(本例中spammodule.c)到Modules/路径下,并且解压缩源代码包,在Modules/Setup.local文 件添加一行描述你的文件: If you can't use dynamic loading, or if you want to make your module a permanent part of the Python interpreter, you will have to change the configuration setup and rebuild the interpreter. Luckily, this is very simple on Unix: just place your file (spammodule.c for example) in the Modules/ directory of an unpacked source distribution, add a line to the file Modules/Setup.local describing your file
spam spammodule.o
并在顶层目录运行make重新构造解释器。你也可以在‘Modules/’路径运行make,但此时你必须首先运行'make Makefile',重新构造Makefile(每次改变Setup文件,必须重新如此)。
如果你的模块需要连接额外的库,这些文件也需要列在配置文件中,如:
spam spammodule.o -lX11
1.6 从C中调用Python函数
到目前为止,我们关注的是从Python中可调用C函数。相反,从C中调用Python函数也是有用的。特 别是在库支持回调函数的情况下。如果C接口使用回调,相应的,Python经常需要为Python 程序员提供回调机制;实现这种机制,将需要从C回调中调用Python回调函数。其他用法也可想像(Other uses are also imaginable)。
幸运的是,Python解释器容易递归调用,并且有一个标准的接口调用Python函数。(我不愿意总是惦记使用一个特别的字符串作为输入,调用Python解释器-除非你感兴趣,在Python源代码中看看 Python/pythonmain.c 文件中-c命令行选项的实现)
调用Python函数是容易的,首先Python程序必须总是传递给你Python函数对象。你应该提供一个函数(或一些别的接口)处理它。当函数被调用时,保存Python函数对象指针(注意Py_INCREF())到一个全局变量-或你认为合适的地方。例如,下面函数也许是模块定义的一部分。
static PyObject *my_callback = NULL;static PyObject *my_set_callback(PyObject *dummy, PyObject *args){ PyObject *result = NULL; PyObject *temp; if (PyArg_ParseTuple(args, "O:set_callback", &temp)) { if (!PyCallable_Check(temp)) { PyErr_SetString(PyExc_TypeError, "parameter must be callable"); return NULL; } Py_XINCREF(temp); /* Add a reference to new callback */ Py_XDECREF(my_callback); /* Dispose of previous callback */ my_callback = temp; /* Remember new callback */ /* Boilerplate to return "None" */ Py_INCREF(Py_None); result = Py_None; } return result;}
函数必须使用METH_VARARGS标志在解释器注册;请参看1.4, ``The Module's Method Table and Initialization Function.''描述。 PyArg_ParseTuple() 函数及其参数参看 1.7, ``Extracting Parameters in Extension Functions.''
宏Py_XINCREF() 和 Py_XDECREF()增加/减少对象的引用计数,并且是NULL指针安全的(但注意temp在这个上下文条件下不为NULL)。关于它们的具体信息参见 1.10, ``Reference Counts.''
然后,是调用函数的时候了,你调用C函数PyEval_CallObject(),这个函数有两个参数,都是指向任意Python对象的指针:Python函数和参数列表。参数列表必须总是元组对象,对象的长度是参数的个数。调用不带参数的Python函数,传递一个空元组;带一个参数调用,传递一个一元元组。当Py_BuildValue()函数中括号中的格式化字符串中包括0或多个格式化代码时,次函数返回一个元组。例如:
int arg; PyObject *arglist; PyObject *result; ... arg = 123; ... /* Time to call the callback */ arglist = Py_BuildValue("(i)", arg); result = PyEval_CallObject(my_callback, arglist); Py_DECREF(arglist);
PyEval_CallObject()返回Python对象指针:这是Python函数的返回值。 PyEval_CallObject()函数对于每个参数是’引用计数无关’的,在这个例子中,新的元组为参数列表而建,在调用PyEval_CallObject()后立即调用Py_DECREF()。
PyEval_CallObject()函数返回值是’新建的’:或者是一个新对象,或者是一个引用计数已经被增加了的存在的对象。所以,除非你想把它保存在全局变量中,否则你总是调用用Py_DECREF()处理结果,无论(尤其是!)你是否关心它的值。
然而,在这么做(调用Py_DECREF)以前检查返回值是否为NULL很重要。如果返回值为NULL,Python函数由于产生异常被终止。如果调用PyEval_CallObject() 函数的C代码是从Python中调用的,现在它应该返回错误指示给Python调用者,这样解释器能够输出堆栈跟踪,或者调用者的Python代码能够处理异常。如果不可能或者想这么做,异常可以通过调用PyErr_Clear()来清除。例如:
if (result == NULL) return NULL; /* Pass error back */ ...use result... Py_DECREF(result);
根据Python回调函数想要的接口,你也许已经提供了参数列表给PyEval_CallObject()。有些情况下,参数列表也可由Python程序通过特定的回调函数接口提供。然后被以和功能对象同样的方式保存和使用。在其他情况下,你必须构造新的元组,作为参数列表传递。最简单的办法是调用Py_BuildValue(),例如,如果你向传递一个整数事件代码,你也许会使用下面的代码:
PyObject *arglist; ... arglist = Py_BuildValue("(l)", eventcode); result = PyEval_CallObject(my_callback, arglist); Py_DECREF(arglist); if (result == NULL) return NULL; /* Pass error back */ /* Here maybe use the result */ Py_DECREF(result);
注意在调用后立刻在错误检查前放置"Py_DECREF(arglist)" !还请注意:这段代码是不完整的: Py_BuildValue()函数可能会产生内存不足,这是应该检查的。
1.7 在扩展函数中提取参数
PyArg_ParseTuple() 函数声明如下:
int PyArg_ParseTuple(PyObject *arg, char *format, ...);
arg参数必须是元组对象,该元组包含从Python传递到C函数的参数列表。 format参数的格式必须是一个格式化的字符串,格式化字符串的语法在"Parsing arguments and building values" 的Python/C API Reference Manual章节中解释,其他参数必须是变量地址,其类型有格式化字符串参数决定。
注意:当PyArg_ParseTuple()函数检查Python参数需要的类型时,不能检查调用传入C变量地址的有效性:如果你输入错误,你的代码也许崩溃,或者至少是改写了内存的随机地址。所以请小心!
注意任何提供给调用者的Python对象引用是borrowed引用,不增加引用计数。
一些调用例子:
int ok; int i, j; long k, l; const char *s; int size; ok = PyArg_ParseTuple(args, ""); /* No arguments */ /* Python call: f() */
ok = PyArg_ParseTuple(args, "s", &s); /* A string */ /* Possible Python call: f('whoops!') */
ok = PyArg_ParseTuple(args, "lls", &k, &l, &s); /* Two longs and a string */ /* Possible Python call: f(1, 2, 'three') */
ok = PyArg_ParseTuple(args, "(ii)s#", &i, &j, &s, &size); /* A pair of ints and a string, whose size is also returned */ /* Possible Python call: f((1, 2), 'three') */
{ const char *file; const char *mode = "r"; int bufsize = 0; ok = PyArg_ParseTuple(args, "s|si", &file, &mode, &bufsize); /* A string, and optionally another string and an integer */ /* Possible Python calls: f('spam') f('spam', 'w') f('spam', 'wb', 100000) */ }
{ int left, top, right, bottom, h, v; ok = PyArg_ParseTuple(args, "((ii)(ii))(ii)", &left, &top, &right, &bottom, &h, &v); /* A rectangle and a point */ /* Possible Python call: f(((0, 0), (400, 300)), (10, 10)) */ }
{ Py_complex c; ok = PyArg_ParseTuple(args, "D:myfunction", &c); /* a complex, also providing a function name for errors */ /* Possible Python call: myfunction(1+2j) */ }