在上一节中,讨论了在用C语言扩展Python模块时,应该如何处理无处不在的引用计数问题。重点关注的是在实现一个C Python的函数时,对于一个PyObject对象,何时调用Py_INCREF和Py_DECREF。在编写C语言代码时,需要了解Python提供的C/C++ API的实现细节,特别是有的API内部实现会调用Py_INCREF,这时自己编写的函数可能需要调用Py_DECREF,而有的API内部实现只是borrowed reference,此时一般不应该调用Py_DECREF。

本节讨论在C扩展Python时,如何对异常和错误进行处理。Python解释器的实现中有一个重要的约定:当一个函数失败,它应该设置一个exception condition并返回一个错误值(通常是NULl指针)。异常是存放在解释器的一个全局变量中,如果这个变量是NULL,那么没有异常发生。另外一个全局变量存放的是跟异常相关的值,还有一个变量包含了stack traceback,记录了产生错误时的Python Code。这三个变量对应Python的sys模块的三个变量,sys.exc_type, sys.exc_value, sys.exc_traceback。在1.5版本之后,这三个变量用exc_info()代替了。

这三个全局的变量在C Python 源码中是存放在PyThreadState *_PyThreadState_Current这个结构体中的, 而_PyThreadState_Current是在pythonrun.c的Py_InitializeEx中初始化的。

PyThreadState结构体定义:

红色框中的三个变量就是error indicator。

常见的Python设置异常的接口

Python API定义了一系列的function来设置不同类型的异常,定义在Python源码中的Python/error.c中。

PyErr_SetString:

最常用的莫过于PyErr_SetString,函数的原型为:

函数的作用是设置Python解释器的全局error indicator。

参数分别为一个exception对象和一个描述异常的字符串。exception object通常是一个已经定义好的object, 不需要增加它的引用计数,在PyErr_SetString源码中已经调用了Py_XINCREF(exception)。

/*Predefined exceptions*/PyAPI_DATA(PyObject*) PyExc_BaseException;

PyAPI_DATA(PyObject*) PyExc_Exception;

PyAPI_DATA(PyObject*) PyExc_StopIteration;

PyAPI_DATA(PyObject*) PyExc_GeneratorExit;

PyAPI_DATA(PyObject*) PyExc_StandardError;

PyAPI_DATA(PyObject*) PyExc_ArithmeticError;

PyAPI_DATA(PyObject*) PyExc_LookupError;

PyAPI_DATA(PyObject*) PyExc_AssertionError;

PyAPI_DATA(PyObject*) PyExc_AttributeError;

PyAPI_DATA(PyObject*) PyExc_EOFError;

PyAPI_DATA(PyObject*) PyExc_FloatingPointError;

PyAPI_DATA(PyObject*) PyExc_EnvironmentError;

PyAPI_DATA(PyObject*) PyExc_IOError;

PyAPI_DATA(PyObject*) PyExc_OSError;

PyAPI_DATA(PyObject*) PyExc_ImportError;

PyAPI_DATA(PyObject*) PyExc_IndexError;

PyAPI_DATA(PyObject*) PyExc_KeyError;

PyAPI_DATA(PyObject*) PyExc_KeyboardInterrupt;

PyAPI_DATA(PyObject*) PyExc_MemoryError;

PyAPI_DATA(PyObject*) PyExc_NameError;

PyAPI_DATA(PyObject*) PyExc_OverflowError;

PyAPI_DATA(PyObject*) PyExc_RuntimeError;

PyAPI_DATA(PyObject*) PyExc_NotImplementedError;

PyAPI_DATA(PyObject*) PyExc_SyntaxError;

PyAPI_DATA(PyObject*) PyExc_IndentationError;

PyAPI_DATA(PyObject*) PyExc_TabError;

PyAPI_DATA(PyObject*) PyExc_ReferenceError;

PyAPI_DATA(PyObject*) PyExc_SystemError;

PyAPI_DATA(PyObject*) PyExc_SystemExit;

PyAPI_DATA(PyObject*) PyExc_TypeError;

PyAPI_DATA(PyObject*) PyExc_UnboundLocalError;

PyAPI_DATA(PyObject*) PyExc_UnicodeError;

PyAPI_DATA(PyObject*) PyExc_UnicodeEncodeError;

PyAPI_DATA(PyObject*) PyExc_UnicodeDecodeError;

PyAPI_DATA(PyObject*) PyExc_UnicodeTranslateError;

PyAPI_DATA(PyObject*) PyExc_ValueError;

PyAPI_DATA(PyObject*) PyExc_ZeroDivisionError;

PyErr_SetObject:

从PyErr_SetString实现的源码中可以看到,内部调用了PyErr_SetObject, PyErr_SetObject内部又调用了PyErr_Restore。

PyErr_SetObject函数原型是:

PyObject* PyErr_Occurred():

函数返回当前是否有异常发生,如果有返回current exception object【borrowed reference】,否则返回NULL

int PyErr_ExceptionMatches(PyObject* exc)

int PyErr_GivenExceptionMatches(PyObject* given, PyObject* exc):

判断给定的异常对象是否符合exc类型。

PyErr_Clear():

清除Python解释器的error indicator。Python解释器不会检测到有异常发生。

PyErr_Fetch(PyObject** ptype, PyObject** pvalue, PyObject** ptraceback)

获得error indicator的三个变量,如果error indicator没有设置,ptype, pvalue, ptraceback都被设置为NULL。error indicator会被置空,将三个变量的地址赋给ptype, pvalue, ptraceback。

PyErr_Restore(PyObject* type, PyObject* value, PyObject* traceback)

使用给定的三个变量设置error indicator。

代码中使用异常接口的规则

如果函数f调用函数g,其中g函数失败,应该怎样设置异常呢?通常的做法是在g中调用设置异常的各个接口,比如PyErr_SetString,通知Python解释器有异常发生了,函数g然后返回一个NULL给函数f,而f不用再处理异常,因为函数g已经上报过了。比如我们自己写的函数调用了PyArg_ParseTuple(),这个函数出现错误时返回NULL,但是我们不用自己上报异常,异常的上报由PyArg_ParseTuple本身处理。一旦通过PyErr_SetString设置了异常,那么Python解释器在主循环中检测到error indicator被设置,会暂停执行当前的Python Code,会试图寻找exception handler来处理异常。

Python源码中使用异常处理接口的例子

PyTuple_GetItem:

PyObject *PyTuple_GetItem(register PyObject*op, register Py_ssize_t i)

{if (!PyTuple_Check(op)) {

PyErr_BadInternalCall();returnNULL;

}if (i < 0 || i >=Py_SIZE(op)) {//如果索引有错误,设置异常

PyErr_SetString(PyExc_IndexError, "tuple index out of range");returnNULL;

}return ((PyTupleObject *)op) ->ob_item[i];

}

PyErr_SetString实现:

voidPyErr_SetString(PyObject*exception, const char *string)

{

PyObject*value = PyString_FromString(string);

PyErr_SetObject(exception, value);

Py_XDECREF(value);

}

PyErr_SetObject实现:

voidPyErr_SetObject(PyObject*exception, PyObject *value)

{

Py_XINCREF(exception);//增加引用计数

Py_XINCREF(value); //增加引用计数

PyErr_Restore(exception, value, (PyObject *)NULL);

}

PyErr_Restore实现:

voidPyErr_Restore(PyObject*type, PyObject *value, PyObject *traceback)

{

PyThreadState*tstate =PyThreadState_GET();

PyObject*oldtype, *oldvalue, *oldtraceback;if (traceback != NULL && !PyTraceBack_Check(traceback)) {/*XXX Should never happen -- fatal error instead?*/

/*Well, it could be None.*/Py_DECREF(traceback);

traceback=NULL;

}/*Save these in locals to safeguard against recursive

invocation through Py_XDECREF*/oldtype= tstate->curexc_type;

oldvalue= tstate->curexc_value;

oldtraceback= tstate->curexc_traceback;//设置当前的异常

tstate->curexc_type =type;

tstate->curexc_value =value;

tstate->curexc_traceback =traceback;//旧的异常变量需要减少引用计数

Py_XDECREF(oldtype);

Py_XDECREF(oldvalue);

Py_XDECREF(oldtraceback);

}

自定义异常

除了使用Python已经定义好的异常对象之外,我们可以自定义异常类型,主要是通过PyErr_NewException创建一个异常对象。

1. 在文件头部定义一个static 变量:

static PyObject *MyError;

2. 在模块初始化时传入我们自定义的异常对象

PyMODINIT_FUNC

inittest(void)

{

PyObject*m;

m= Py_InitModule("test", TestMethods);if (m ==NULL)return;

MyError= PyErr_NewException("test.error", NULL, NULL);

Py_INCREF(MyError);

PyModule_AddObject(m,"error",MyError);

}

3. 在通过PyErr_SetString设置异常时,第一个参数传入MyError即可。

python 异常处理模块_扩展Python模块系列(五)----异常和错误处理相关推荐

  1. python 参数封装_扩展Python模块系列(三)----参数解析与结果封装

    在上一节中,通过一个简单的例子介绍了C语言扩展Python内建模块的整体流程,从本节开始讲开始深入讨论一些细节问题,在细节讨论中从始至终都会涉及[引用计数]的问题.首先讨论C语言封装的Python函数 ...

  2. python缩写词_扩展Python中的英语缩略词

    我把wikipedia的扩展页面压缩成python字典(见下文) 请注意,如您所料,在查询字典时,您一定要使用双引号: 另外,我在维基百科页面中留下了多个选项.你可以随意修改它.注意,对右展开的消歧将 ...

  3. python 时间序列预测_使用Python进行动手时间序列预测

    python 时间序列预测 Time series analysis is the endeavor of extracting meaningful summary and statistical ...

  4. python 概率分布模型_使用python的概率模型进行公司估值

    python 概率分布模型 Note from Towards Data Science's editors: While we allow independent authors to publis ...

  5. SpringBoot系列五:SpringBoot错误处理(数据验证、处理错误页、全局异常)

    SpringBoot系列五:SpringBoot错误处理(数据验证.处理错误页.全局异常) 参考文章: (1)SpringBoot系列五:SpringBoot错误处理(数据验证.处理错误页.全局异常) ...

  6. python中怎样使用re模块_[转]Python中RE模块的应用

    Python 自1.5版本起增加了re 模块,它提供 Perl 风格的正则表达式模式.Python 1.5之前版本则是通过 regex 模块提供 Emecs 风格的模式.Emacs 风格模式可读性稍差 ...

  7. python哪个关键字可以导入模块_关于python导入模块import与常见的模块详解

    0.什么是python模块?干什么的用的? Java中如果使用abs()函数,则需要需要导入Math包,同样python也是封装的,因为python提供的函数太多,所以根据函数的功能将其封装在不同的m ...

  8. c调用python代码找不到模块_构建 Python C 扩展模块

    有好几种扩展 Python 的功能的方法.其中一种就是用 C 或 C++ 编写 Python 模块.通过这个过程可以提高性能,更好地访问 C 库函数和系统调用.在本教程中,我将带大家了解如何使用 Py ...

  9. python中tkinter模块_使用Python中的tkinter模块作图的方法

    python简述: Python是一种解释型.面向对象.动态数据类型的高级程序设计语言.自从20世纪90年代初Python语言诞生至今,它逐渐被广泛应用于处理系统管理任务和Web编程.Python[1 ...

最新文章

  1. python mysqldb cursor_python中MySQLdb模块用法实例
  2. 思考题目,仔细检查,外加一个ceil函数
  3. 第五届新疆ACM H-虚无的后缀
  4. linux git diff patch,拿到git patch要怎麼用一般patch指令merge?
  5. cad里面f命令用不了_CAD出现命令无效、失灵等问题?不用慌,两招帮你快速解决...
  6. AC自动机 - 关于Fail指针
  7. python生成随机数random操作_Python random生成随机数示例
  8. python刚出来多少薪资-作为Python程序员,薪资一般是多少?
  9. ubuntu18.04 安装 腾讯qq
  10. VSTO安装卸载方法
  11. mac下的c语言程序开发,mac VS Code配置C语言开发环境(小白简单Clang版)
  12. c语言实现一个计算器
  13. 我的VSTO之路(五):Outlook初步开发之联系人扩展
  14. Ubuntu16.04+Cuda9.0+Cudnn7.0+python2.7+Caffe
  15. rxJava中 Subscriber 与Observer
  16. 统计|如何理解多元回归下的多重可决系数
  17. matlab fabs能用吗,为什么使用abs()或fabs()代替条件否定?
  18. (好文重发)朴英敏:用crash工具分析Linux内核死锁的一次实战
  19. burpsuit无法成功代理之导入证书
  20. 父债子偿有法可依吗?可法院却对这个案子说:不!

热门文章

  1. Connection to node 0 (/192.168.204.131:9092) could not be established
  2. linux下生成源程序控制流图,Linux下控制(统计)文件的生成的C代码实现
  3. NXP(I.MX6uLL)DDR3实验——DDR3重要时间参数、时钟配置与原理图简析
  4. mysql 导入json_JsonToMysql(json导入mysql数据库工具)
  5. linux下makefile中cp,make与makefile 的理解
  6. oracle导出数据dummy,oracle导出表结构1
  7. Linux 运维必备150 个命令,值得收藏!
  8. C#通过SMTP发送邮件代码示例
  9. python自定义colorbar_python可视化 matplotlib画图使用colorbar工具自定义颜色
  10. 计算机应用用什么样的笔记本,制图用什么笔记本好