基于抽象基类捕获异常

时间:2014-05-27 13:24:11

标签: python exception abstract-base-class

假设我有一个带有抽象基类的异常类,如下所示:

class MyExceptions(BaseExeption, metaclass=abc.ABCMeta):
    pass

class ProperSubclass(MyExceptions):
    pass

MyExceptions.register(ValueError)

我似乎可以ProperSubclass抓住MyExceptions,但ValueError抓不到try: raise ProperSubclass() except MyExceptions: print('As expected, control comes here...') except: print('...and not here.') try: raise ValueError() except MyExceptions: print('Control does not come here...') except ValueError: print('...but unexpectedly comes here.')

{{1}}

所以我的问题是,我是否应该能够通过抽象基类捕获内置异常?如果是这样,怎么样?如果没有,那么规则是什么?

我想另一种问这个问题的方法是:除了条款正确使用isinstance()/ issubclass()进行匹配,如果没有(看起来是这样),他们使用的是做什么?也许在C实现中有一些阴暗的快捷方式。

1 个答案:

答案 0 :(得分:7)

documentation说:

  

如果对象是异常对象的类或基类,或者包含与异常兼容的项的元组,则该对象与异常兼容。

不幸的是,这并没有说明是否应该考虑虚拟基类,这与例如issubclass

  

如果 class classinfo 的子类(直接,间接或虚拟),则返回true。 [...]

覆盖instance and subclass checks的语言也没有多大帮助:

  

以下方法用于覆盖isinstance()issubclass()内置函数的默认行为。 [...]

事实上,正如您所怀疑的那样,CPython实现(针对Python 3)绕过子类检查,直接调用PyType_IsSubtype

http://hg.python.org/cpython/file/3.4/Python/errors.c#l167

PyErr_GivenExceptionMatches(PyObject *err, PyObject *exc)
{
    ...
        /* PyObject_IsSubclass() can recurse and therefore is
           not safe (see test_bad_getattr in test.pickletester). */
        res = PyType_IsSubtype((PyTypeObject *)err, (PyTypeObject *)exc);

作为参考,issubclass PyObject_IsSubclass的CPython实现在回到__subclasscheck__之前调用PyType_IsSubtype

所以这种行为有充分的理由;异常处理需要是非递归的,因此它回调到Python代码是不安全的。请注意,Python 2.7 version接受溢出的风险并调用PyObject_IsSubclass。 Python 3中有一个proposal to relax this restriction,但是虽然已经编写了一个补丁但它还没有被接受。否则,文档最好澄清except检查是非虚拟的。