考虑以下使用__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__。
发布于 2020-09-21 06:27:59
我想这是一个CPython实现细节。如PyErr_NormalizeException文档中所述
在某些情况下,下面
PyErr_Fetch()返回的值可能是“未规范化的”,这意味着*exc是类对象,但*val不是同一个类的实例。
因此,在处理引发的错误时,CPython会将异常规范化,因为否则它不能假定错误的值是正确的类型。
就您的情况而言,情况如下:
PyErr_Print,其中它是呼叫 _PyErr_NormalizeException。_PyErr_NormaliizeException 呼叫 PyObject_IsSubclass.PyObject_IsSubclass,则使用__subclasscheck__。我不能说"*exc是类对象,但*val不是同一个类的实例“的”特定情况“是什么(可能是向后兼容性所需要的--我不知道)。
我的第一个假设是,当CPython确保(即这里)异常来自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))也就是说,只查看tpflags。
https://stackoverflow.com/questions/63928368
复制相似问题