python - 为什么引发异常会调用 __subclasscheck__?

标签 python python-3.x exception cpython

考虑以下使用 __subclasscheck__ 的示例对于自定义异常类型:

class MyMeta(type):
    def __subclasscheck__(self, subclass):
        print(f'__subclasscheck__({self!r}, {subclass!r})')

class MyError(Exception, metaclass=MyMeta):
    pass
现在,当引发这种类型的异常时,__subclasscheck__方法被调用;即 raise MyError()结果是:
__subclasscheck__(<class '__main__.MyError'>, <class '__main__.MyError'>)
Traceback (most recent call last):
  File "test.py", line 8, in <module>
    raise MyError()
__main__.MyError
这里输出的第一行显示 __subclasscheck__被调用以检查是否 MyError是自身的子类,即 issubclass(MyError, MyError) .我想了解为什么这是必要的以及它通常是如何有用的。

我正在使用 CPython 3.8.1 来重现这种行为。我也试过 PyPy3 (3.6.9) 和这里 __subclasscheck__不被调用。

最佳答案

我想这是一个 CPython 实现细节。如文档所述 PyErr_NormalizeException :

Under certain circumstances, the values returned by PyErr_Fetch() below can be “unnormalized”, meaning that *exc is a class object but *val is not an instance of the same class.


所以在处理引发的错误的某个时候,CPython 会规范化异常,否则它无法假设错误的值是正确的类型。
在你的情况下,它发生如下:
  • 最终在处理异常时, PyErr_Print 被调用,它在哪里calls _PyErr_NormalizeException .
  • _PyErr_NormaliizeException calls PyObject_IsSubclass .
  • PyObject_IsSubclass 用途 __subclasscheck__如果提供。

  • 我不能说“*exc 是类对象但 *val 不是同一类的实例”的那些“某些情况”是什么(可能需要向后兼容 - 我不知道)。

    我的第一个假设是,它发生在 CPython 确保(即 here )时,异常源自 BaseException .
    以下代码
    class OldStyle():
        pass
    
    raise OldStyle
    
    会提出 OldStyle对于 Python2,但是 TypeError: exceptions must be old-style classes or derived from BaseException, not type为了
    class NewStyle(object):
        pass
    
    raise NewStyle
    
    TypeError: exceptions must derive from BaseException在 Python3 中,因为在 Python3 中所有类都是“新风格”。
    但是,对于此检查不是 PyObject_IsSubclass但是 PyType_FastSubclass 用来:
    #define PyExceptionClass_Check(x)                                       \
        (PyType_Check((x)) &&                                               \
         PyType_FastSubclass((PyTypeObject*)(x), Py_TPFLAGS_BASE_EXC_SUBCLASS))
    
    即只有 tpflag s被看着。

    关于python - 为什么引发异常会调用 __subclasscheck__?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63928368/

    相关文章:

    java - 使用 Boolean AND 时显式评估两个条件

    Python 字符串模板 - 使用百分比分隔符

    python-3.x - Networkx-索引错误 : list index out of range while using (greedy_modularity_communities

    c++ - 构造函数不抛出异常

    spring-boot - Spring WebFlux Reactive 和 Kotlin Coroutines 启动报错

    python - 参数 num_class 的 xgboost sklearn 包装器值 0 应大于等于 1

    python - 使用 numpy 数组的 sympy 数字

    python - 如何在python中循环遍历csv文件并将csv文件的每个部分输出到新文件中?

    c# - ExecuteScalar "Specified cast is not valid"异常

    python - 使用预训练嵌入的基于词集的词义消歧