Ловля исключений на основе их абстрактного базового класса
Предположим, у меня есть класс исключений с абстрактным базовым классом, что-то вроде этого:
class MyExceptions(BaseExeption, metaclass=abc.ABCMeta):
pass
class ProperSubclass(MyExceptions):
pass
MyExceptions.register(ValueError)
Похоже, что я могу поймать ProperSubclass
от MyExceptions
, но нет 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(), за исключением предложений, и если нет (как это имеет место), что они используют? Возможно, в реализации языка Си есть несколько сомнительных ярлыков.
1 ответ
В документации сказано:
Объект совместим с исключением, если это класс или базовый класс объекта исключения или кортеж, содержащий элемент, совместимый с исключением.
К сожалению, это не говорит о том, следует ли рассматривать виртуальные базовые классы, в отличие от языка, например для issubclass:
Верните true, если класс является подклассом (прямым, косвенным или виртуальным) classinfo. [...]
Язык переопределения проверок экземпляров и подклассов тоже мало помогает:
Следующие методы используются для переопределения поведения по умолчанию
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
чеки не виртуальные.