《扩展和嵌入python解释器》1.2 Intermezzo: 错误和异常

1.2 Intermezzo: 错误和异常

下面是整个Python解释器的一个重要惯例:当一个函数发生错误时,它应该设置一个异常状态并返回一个错误值(通常是NULL指针)。异常存储在解释器内部的一个静态的全局变量中;如果这个全局变量值为NULL,表示没有异常发生,第二个全局变量存储异常(raise的第二个参数)的相关值。第三个变量存储在Python代码中发生错误时堆栈反向跟踪数据。这三个变量是Python变量sys.exc_ type, sys.exc_value sys.exc_traceback(参看Python Library Referencesys模块部分)对应的C变量。了解这些变量对于理解错误如何传递是非常重要的。

Python API定义了一些函数用来设置异常的各种类型。

最重要的一个是PyErr_SetString()。它的参数是一个异常对象和一个C字符串。异常对象通常是一个象PyExc_ZeroDivisionError一样的预定义对象。C字符串指示错误的原因并被转换为Python字符串对象存储在异常的相关值’associated value’)中。

另一个有用的函数是PyErr_SetFromErrno(),它只带一个异常参数,并通过检查全局变量errno来构造相关值。最常用的函数是PyErr_SetObject(),它带两个对象参数:异常和异常的相关值。你不必Py_INCREF()对象传递给这些函数。

检测non-destructively检测异常是否被PyErr_Occurred()函数设置。此函数[1]返回当前异常对象或在没有异常产生时返回NULL。一般的,因为你可以通过函数返回值识别错误,所以不必调用PyErr_Occurred()函数去查看是否在一个函数调用中有错误产生。

当函数F调用另一个函数G,并且检测到后面的函数-G有错误,函数F应当返回一个错误值(通常为NULL-1)。函数F应该不调用PyEr_*()函数之一 ――其中一个已经在G中调用。F的调用者必须也返回一个错误通知它的调用者,也不需要调用PyEr_*()函数,依次类推――最详细的错误信息已经由最先探测到错误的函数报告了。一旦错误到达Python解释器的主循环,这将终止当前执行的Python代码,并试图寻找Python程序员指定的异常句柄。

(在有些条件下,模块能够通过调用PyEr_*()函数给出确切的错误消息,并且在这种情况下,这么做很好。然而,作为一般规则,这是没有必要的,并且会导致错误信息丢失:由于各种原因,大多数操作都是失败的。)

为了忽略由于函数调用失败而产生的异常,异常条件必须通过明确调用函数PyErr_Clear()来清除。仅当不想传递错误给解释器而想要完全自己处理错误时,C代码才调用PyErr_Clear()函数。(可能是由于尝试些别的东西,或装做没有错误发生)

每次调用malloc()失败必须进入异常-malloc()直接调用者必须调用PyErr_NoMemory()并返回指示它自己错误的标志。所有的对象创建函数(如:PyInt_FromLong())都已经这们做了,所以这条规则只和那些直接调用malloc()的函数相关。

还有一点,PyArg_ParseTuple()和相似函数产生的异常,带有整数状态返回值的函数都认为返回值-1表示失败,0或整数表示成功,就象UNIX系统调用一样。

最后,当你返回一个错误指示时,小心地清除垃圾(通过调用你创建的对象的Py_XDECREF() Py_ DECREF()函数)。

产生哪个异常完全由你自己选择。有一些预先声明的C对象处理所有内置Python异常,如PyExc_ZeroDivisionError:你可以直接使用。当然,你应合理明智地选择异常,不要使用PyExc_TypeError表示文件不能打开(而应该使用PyExc_IOError)。如果参数列表发生一些错误,函数PyArg_ParseTuple()通常产生PyExc_TypeError错误。如果你有的参数必须在特定范围内,或必须满足其他条件,应该使用PyExc_ValueError异常。

你也可以在自己的模块中定义独一无二的新异常。为此,你应该在你的文件开始处声明一个静态的对象变量:

static PyObject *SpamError;

并且在你的模块的初始化函数(initspam())中用一个异常对象初始化它(不考虑错误检查)。

 

PyMODINIT_FUNC
initspam(void)
{
    PyObject *m;

    m = Py_InitModule("spam", SpamMethods);

    SpamError = PyErr_NewException("spam.error", NULL, NULL);
    Py_INCREF(SpamError);
    PyModule_AddObject(m, "error", SpamError);
}

请注意:Python中异常的名称为spam.error.PyErr_NewException()函数可以从一个异常基类派生的新类(除非传入另一个类而不是NULL),《Python Library Reference》内置异常(Built-in Exceptions)中有详述。

 

还请注意: SpamError 变量保持着新创建异常的引用;这是故意的!由于异常能够被外部代码从模块中删除,类的引用的拥有者必须确认引用对象没有被释放,导致 SpamError 成为野指针。产生异常的 C 代码一起内核转储或其他不可预料的后果。下面,我们将在这个例子中讨论把 PyMODINIT_FUN 作为函数的返回类型。


[1] 这个函数的返回值是这样的吗?

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值