Ловля исключений на основе их абстрактного базового класса

Предположим, у меня есть класс исключений с абстрактным базовым классом, что-то вроде этого:

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 чеки не виртуальные.

Другие вопросы по тегам