【问题标题】:Catching exceptions based on their abstract base class基于抽象基类捕获异常
【发布时间】:2014-05-27 13:24:11
【问题描述】:

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

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

class ProperSubclass(MyExceptions):
    pass

MyExceptions.register(ValueError)

看来我可以通过MyExceptions 捕捉到ProperSubclass,但不是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 实现中存在一些阴暗的捷径。

【问题讨论】:

    标签: python exception abstract-base-class


    【解决方案1】:

    documentation 说:

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

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

    如果 classclassinfo 的子类(直接、间接或 虚拟),则返回 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 的 CPython 实现 PyObject_IsSubclass 在回退到 PyType_IsSubtype 之前调用 __subclasscheck__

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

    【讨论】:

    • 这就是我害怕的。感谢您跟踪。
    • 请注意,如果您使用 Python2.7,PyErr_GivenExceptionMatches 实际上确实调用了PyObject_IsSubclass,它会起作用。
    • 如需更深入的讨论,另请参阅 bugs.python.org/issue12029,Georg Brandl 提出了一个草案提案,通过仔细处理 PyObject_IsSubclass 调用周围的异常状态来解除此限制。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-02-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-18
    • 2018-07-11
    • 1970-01-01
    相关资源
    最近更新 更多