您的位置:首页 > 编程语言 > C语言/C++

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

2015-04-21 13:46 211 查看
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_FUNC
initspam(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_FUNC
initspam(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. 类、对象、方法、属性

未完,待续
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: