考虑以下使用 __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/