基于抽象基类捕获异常

abi*_*ham 8 python exception abstract-base-class

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

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

class ProperSubclass(MyExceptions):
    pass

MyExceptions.register(ValueError)
Run Code Online (Sandbox Code Playgroud)

看来,我能赶上ProperSubclassMyExceptions,但不是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.')
Run Code Online (Sandbox Code Playgroud)

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

我想提出这个的另一种方式是:为匹配做,除非条款正确使用isinstance()/ issubclass(),如果没有(如出现这种情况)什么他们使用?也许在C实现中有一些阴暗的快捷方式.

eca*_*mur 7

文件说:

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

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

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

重写实例和子类检查的语言也没有多大帮助:

下面的方法用于覆盖的默认行为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);
Run Code Online (Sandbox Code Playgroud)

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

所以这种行为有充分的理由; 异常处理需要是非递归的,因此它回调到Python代码是不安全的.请注意,Python 2.7版本接受溢出的风险并进行调用PyObject_IsSubclass.有人建议在Python 3中放宽这个限制,但是虽然已经编写了补丁但尚未被接受.否则,文档澄清except检查是非虚拟的是一个好主意.