A Simple Example¶
这个example的目的是写一个在python中调用c中system的module,用途如下
>>> import spam
>>> status = spam.system("ls -l")
我们应该这样引入Python的API
#define PY_SSIZE_T_CLEAN
#include <Python.h>
Notice
这两行通常应该放在其它所有引用之前。
下面一步是写我们要在python中调用的C function,例子如下
static PyObject * spam_system(PyObject *self, PyObject *args)
{
const char *command;
int sts;
if (!PyArg_ParseTuple(args, "s", &command))
return NULL;
sts = system(command);
return PyLong_FromLong(sts);
}
这里的PyArg_ParseTuple将PyObject经检查后由指定格式转换为C对象。
如果我们需要写一个没有返回值的函数,也就是void型的函数,对应的Python函数必须返回None,我们需要写下面的代码
Py_INCREF(Py_None);
return Py_None;
Py_RETURN_TRUE代替。
Py_None在C中相当于Python中的None。
错误和异常¶
Python解释器有这样的convention:当一个函数出现问题的时候,应该设定一个异常条件并返回错误的值。异常信息储存在解释器线程状态的三个数字之中,如果没有exception的时候,它们是NULL,否则它们和sys.exc_info()返回的Python tuple是一样的,tuple中的三个数字分别是(exception type, exception instance, traceback object)。
Python API中定义了一堆设定不同类型异常的函数。
最常用的一种是PyErr_SetString()。参数是一个定义好了的异常对象和一个C-string。这个C-string说明了异常发生的原因同时它被转换成了一个python string对象并存在exception的“associated value”中。
另一个常用的是PyErr_SetFromErrno。它只接受一个异常作为参数并根据全局变量errno构建associated value。
最一般的函数是PyErr_SetObject,它接受两个对象作为参数:异常和associated value。
The Module's Method Table and Initialization Function¶
我们需要在Python中调用span_system()这个函数,首先要将函数的名字和地址列在一个“method table”中。
static PyMethodDef SpamMethods[] = {
{"system", spam_system, METH_VARARGS, "Execute a shell commmand"},
{NULL, NULL, 0, NULL} // Sentinel item
}
注意那个METH_VARARGS,这个东西告诉interpreter对应C函数的调用方式,通常是METH_VARARGS和METH_VARARGS | METH_KEYWORDS。当使用METH_VARARGS的时候,这个函数except对应Python-level的用tuple传进来的蚕食,可以通过PyArg_ParseTuple来parsing。
METH_KEYWORDS这个flag让函数可以使用Keyword。
Method table 需要在一个module definition structure中引用,示例代码如下
static struct PyModuleDef spammodule = {
PyModuleDef_HEAD_INIT,
"spam", /* name of module */
spam_doc, /* module documentation, may be NULL */
-1, /* size of per-interpreter state of the module,
or -1 if the module keeps state in global variables. */
SpamMethods
};
最后,这个module definition structure应该被传到一个module's initialization function中。这个initialization function应该被声明为PyInit_name(),这里的name就是对应的module的名字,函数也应该被声明为non-static的,实例代码如下。
PyMODINIT_FUNC
PyInit_spam(void) {
return PyModule_Create(&spammodule);
}
当一个python程序import spam的时候,PyInit_spam被调用,它调用PyModule_Create(),这个函数会返回一个指向它创建的module object的指针,并基于对应的信息将函数什么的插入进module中。
当Embedding Python中的时候,PyInit_spam()这个函数不会被自动调用,除非在表P有Import_Inittab中有一个对应的entry。为了将module加入initialization table,需要用到PyImport_AppendInittab()。实例代码如下:
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);
}
/* Add a built-in module, before Py_Initialize */
if (PyImport_AppendInittab("spam", PyInit_spam) == -1) {
fprintf(stderr, "Error: could not extend in-built modules table\n");
exit(1);
}
/* Pass argv[0] to the Python interpreter */
Py_SetProgramName(program);
/* Initialize the Python interpreter. Required.
If this step fails, it will be a fatal error. */
Py_Initialize();
/* Optionally import the module; alternatively,
import can be deferred until the embedded script
imports it. */
PyObject *pmodule = PyImport_ImportModule("spam");
if (!pmodule) {
PyErr_Print();
fprintf(stderr, "Error: could not import module 'spam'\n");
}
...
PyMem_RawFree(program);
return 0;
}
编译和链接¶
有两个重要的事情:compiling and linking。
如果不用动态加载的话,或者所如果想让module变成Python interpreter的一部分,这时需要更改configuration setup并rebuild interpreter。
在Windows build extensions有两种方式,用distutils或者手工实现
Calling Python Functions from C¶
还有一个重要的事情就是在C中调用Python中的函数。
首先,Python程序必须以某种方式传进来Python对象,这需要一个函数去做。当调用这个函数时,需要将一个指向函数对象的指针保存到一个全局变量中。例如下面的示例程序。
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这个标签。Py_XINCREF和Py_XDECREF这两个宏用于增加或减少对应对象的引用计数,并且它们是safe for NULL的。
当我们想要调用这个函数的时候,可以调用PyObject_CallObject,这个函数有两个参数,类型均为指向PyObject的指针,一个是对应的Python函数,另一个是参数列表。参数列表必须是一个tuple,它的长度就是对应的参数的数量,没有参数的时候传进去一个NULL就行了。可以用Py_BuildValue来创建tuple。实例代码如下
int arg;
PyObject *arglist;
PyObject *result;
...
arg = 123;
...
/* Time to call the callback */
arglist = Py_BuildValue("(i)", arg);
result = PyObject_CallObject(my_callback, arglist);
Py_DECREF(arglist);
PyObject_CallObject()返回一个Python object的指针,也就是对应的Python函数的返回值。这个函数还是关于它的参数"reference-count-neutral"的。
这里这个Py_DECREF(PyObject* o)函数的作用是将对应对象的引用计数减一,如果对应的reference count减到0,这个函数的deallocation function就会执行。这个东西要求参数不是NULL,如果不能确定是不是NULL,需要用Py_XDECREF()。
PyObject_CallObject()的返回值是"new"的:他既是一个新的对象,也是一个引用计数增加了的已经存在了的对象。
注意要检查返回值是否是NULL的。如果是NULL的话,因为这调用的是一个Python函数,如果它出错了,那么此时一定已经raising an exception。
调keyword function用PyObject_Call()。