12

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

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

class ProperSubclass(MyExceptions):
    pass

MyExceptions.register(ValueError)

看来我可以赶上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.')

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

我想问这个问题的另一种方式是:除了子句是否正确使用 isinstance()/issubclass() 进行匹配,如果没有(似乎是这种情况)它们使用什么也许在 C 实现中有一些阴暗的捷径。

4

1 回答 1

15

文档说:

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

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

如果class是classinfo的子类(直接、间接或虚拟),则返回 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);

作为参考,issubclass 的 CPython 实现PyObject_IsSubclass__subclasscheck__回退到PyType_IsSubtype.

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

于 2014-05-27T14:14:05.837 回答