Extending Python with C\C++ 实践问题

来源:互联网 发布:数据挖掘工程师面试题 编辑:程序博客网 时间:2024/05/16 17:19

python doc:Extending Python

根据上面的内容,可编写如下程序:

#include <Python.h>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 Py_BuildValue("i", sts);}static PyMethodDef SpamMethods[] = {    {"system",  spam_system, METH_VARARGS,"Execute a shell command."},    {NULL, NULL, 0, NULL}};PyMODINIT_FUNCinitspam(void){    Py_InitModule("spam", SpamMethods);}

利用codeblocks新建工程,编译dll, 具体操作步骤可参考Python之美[从菜鸟到高手]。通过这篇文章,我成功在python里调用到了spam.system(‘ls -s’)。

1 标准异常

阅读python官方文档Extending Python with C\C++后,第二小节是关于异常的,我试着在上面代码中加入异常的语句,如下:

#include <Python.h>static PyObject *spam_system(PyObject *self, PyObject *args){    const char *command;    int sts;    if (!PyArg_ParseTuple(args, "s", &command))        return NULL;    sts = system(command);    if (sts > 0) //添加异常,如果系统没有传入的命令,返回值不为0    {        //这里的异常PyExc_ArithmeticError是我随便选的预定义异常对象        PyErr_SetString(PyExc_ArithmeticError, "something is wrong");        return NULL;    }    return Py_BuildValue("i", sts);}static PyMethodDef SpamMethods[] = {    {"system",  spam_system, METH_VARARGS,"Execute a shell command."},    {NULL, NULL, 0, NULL}};PyMODINIT_FUNCinitspam(void){    Py_InitModule("spam", SpamMethods);}

编译出错,出现错误:undefined reference to _imp__PyErr_SetString, 缺少库文件,需要把libpython26.a添加到codeblocks的链接库中,和python26.lib一块。然后就可以编译成功了。其中的PyExc_ArithmeticError是预定义的一些标准异常,可参考标准异常。编译成功后,在python中调用如下:

>>>import spam>>> spam.system('lll')Traceback (most recent call last):  File "<pyshell#12>", line 1, in <module>    spam.system('lll')ArithmeticError: something is wrong>>> spam.system('ls -s')0>>> 

2 自定义异常

下面就是自定义异常:

#include <Python.h>static PyObject *SpamError;static PyObject *spam_system(PyObject *self, PyObject *args){    const char *command;    int sts;    if (!PyArg_ParseTuple(args, "s", &command))        return NULL;    sts = system(command);    if (sts > 0)    {        PyErr_SetString(SpamError, "something is wrong");        return NULL;    }    return Py_BuildValue("i", sts);}static PyMethodDef SpamMethods[] = {    {"system",  spam_system, METH_VARARGS,"Execute a shell command."},    {NULL, NULL, 0, NULL}};PyMODINIT_FUNC initspam(void){    PyObject *m;    m = Py_InitModule("spam", SpamMethods);    if (m == NULL)        return;    SpamError = PyErr_NewException("spam.error111", NULL, NULL);    Py_INCREF(SpamError);    PyModule_AddObject(m, "error222", SpamError);}

编译后,在python中调用,结果:

>>> import spam>>> spam.system('lll')Traceback (most recent call last):  File "<pyshell#15>", line 1, in <module>    spam.system('lll')error111: something is wrong>>>

异常的名字为error111,异常信息something is wrong。当在python IDLE中导入spam,可发现:

>>> spam.error222<class 'spam.error111'>>>> 

spam.error111是异常对象SpamError所属异常类的名字,PyModule_AddObject则把对象SpamError添加到模块m(spam模块)的属性字典中,key是error222。

3 其它

1. PyErr_Occurred()

PyErr_Occurred 用来判断是否产生了一个异常,如果没有,返回NULL,PyErr_Occurred() == NULL 返回True;如果有异常,该函数返回最近的一个异常对象,可使用PyErr_ExceptionMatches()进行对比:

    if (sts > 0)    {        PyErr_SetString(SpamError, "something is wrong");        if(PyErr_ExceptionMatches(SpamError))        {            PyErr_SetString(PyExc_ArithmeticError, "PyExc_ArithmeticError");            return NULL;        }        return NULL;    }    return Py_BuildValue("i", sts);

用上面代码替换第2节对应位置,编译后,在python执行:

>>> import spam>>> spam.system('lll')Traceback (most recent call last):  File "<pyshell#19>", line 1, in <module>    spam.system('lll')ArithmeticError: PyExc_ArithmeticError>>> 

返回ArithmeticError而不是SpamError。

2. PyErr_Clear()

如果要忽略不处理异常,可调用PyErr_Clear():

    if (sts > 0)    {        PyErr_SetString(SpamError, "something is wrong");        PyErr_Clear();        return NULL;    }    return Py_BuildValue("i", sts);

编译后,python调用:

>>> import spam>>> spam.system('lll')Traceback (most recent call last):  File "<pyshell#21>", line 1, in <module>    spam.system('lll')SystemError: error return without exception set>>> 

上面这个异常,可能是return NULL造成的,前面的异常被忽略了。

3. PyArg_ParseTuple()

这个函数是用来解析元组参数的。前面的代码部分:

    if (!PyArg_ParseTuple(args, "s", &command))        return NULL;

“s”表示传入一个字符串参数,即python调用为:

>>>spam.system('ls -s')

而如果修改格式字符串:

    if (!PyArg_ParseTuple(args, "(s)", &command))        return NULL;

就表示传入一个元组参数,即python调用为:

>>> spam.system(('ls -s',))0>>>

然后可以解析元组的多个参数,修改C代码如下:

static PyObject *spam_system(PyObject *self, PyObject *args){    const char *command1;    const char *command2;    int sts1, sts2;    if (!PyArg_ParseTuple(args, "(ss)", &command1, &command2))        return NULL;    sts1 = system(command1);    sts2 = system(command2);    if (sts1 > 0)    {        PyErr_SetString(SpamError, "something is wrong");        PyErr_Clear();        return NULL;    }    return Py_BuildValue("(ii)", sts1, sts2);}

编译后,python调用如下:

>>> spam.system(('ls -s', 'ls -s'))(0, 0)>>> 

返回值为一个元组,对应每一个命令的返回值。s或者i分别表示字符串和整数,具体可参考Python官方文档参数解析及值的构造,其中包含了各种类型的格式化字符。
继续实践,字符串和整数的:

    const char *command1;    const char *command2;    int sts1, sts2, temp;    temp = 0;    if (!PyArg_ParseTuple(args, "(ssi)", &command1, &command2, &temp))        return NULL;    sts1 = system(command1);    sts2 = system(command2);    printf("%d\n", temp);    return Py_BuildValue("(iii)", sts1, sts2, temp);

python调用为:

>>> import spam>>> spam.system(('ls -s', 'ls -s', 4))(0, 0, 4)>>> 

注意:PyArg_ParseTuple()的后面的参数,都是一些地址!

4. 类、对象、方法、属性

未完,待续

0 0
原创粉丝点击