Правильный способ объявления пользовательских исключений в современном Python?

avatar
Nelson
23 августа 2009 в 21:29
783439
14
1498

Как правильно объявлять собственные классы исключений в современном Python? Моя основная цель - следовать тому стандарту, который есть в других классах исключений, чтобы (например) любая дополнительная строка, которую я включаю в исключение, распечатывалась любым инструментом, перехватывающим исключение.

Под «современным Python» я подразумеваю то, что будет работать в Python 2.5, но будет «правильным» для Python 2.6 и Python 3. *. И под «индивидуальным» я подразумеваю объект Exception, который может включать дополнительные данные о причине ошибки: строку, возможно, также какой-либо другой произвольный объект, имеющий отношение к исключению.

Меня сбило с толку следующее предупреждение об устаревании в Python 2.6.2:

>>> class MyError(Exception):
...     def __init__(self, message):
...         self.message = message
... 
>>> MyError("foo")
_sandbox.py:3: DeprecationWarning: BaseException.message has been deprecated as of Python 2.6

Кажется безумным, что BaseException имеет особое значение для атрибутов с именем message. Насколько я понимаю, из PEP-352 этот атрибут имел особое значение в версии 2.5, которую они пытаются исключить, так что я предполагаю, что это имя (и только это) теперь запрещено? Уф.

Я также смутно знаю, что Exception имеет какой-то магический параметр args, но я никогда не знал, как его использовать. Я также не уверен, что это правильный путь в будущем; Многие обсуждения, которые я нашел в Интернете, предполагали, что они пытались избавиться от аргументов в Python 3.

Обновление: в двух ответах предлагается переопределить __init__ и __str__ / __unicode__ / __repr__. Кажется, нужно много печатать, нужно ли это?

Источник
Anakhand
29 июня 2020 в 12:07
10

Я считаю, что это один из тех случаев, когда Python не следует одному из своих афоризмов: There should be one-- and preferably only one --obvious way to do it.

Ответы (14)

avatar
gahooa
23 августа 2009 в 21:55
1554

Может я пропустил вопрос, но почему бы и нет:

class MyException(Exception):
    pass

Чтобы что-то переопределить (или передать дополнительные аргументы), сделайте следующее:

class ValidationError(Exception):
    def __init__(self, message, errors):            
        # Call the base class constructor with the parameters it needs
        super().__init__(message)
            
        # Now for your custom code...
        self.errors = errors

Таким образом, вы можете передать dict сообщений об ошибках второму параметру и получить к нему позже с помощью e.errors.

В Python 2 вам нужно использовать эту немного более сложную форму super():

super(ValidationError, self).__init__(message)
jiakai
1 августа 2017 в 02:54
51

Однако исключение, определенное таким образом, не может быть выбрано; см. обсуждение здесь coderhelper.com/questions/16244923/…

Robino
15 сентября 2017 в 13:39
128

@jiakai означает «маринованный». :-)

ddleon
27 февраля 2020 в 15:13
3

Согласно документации python для пользовательских исключений, имена, упомянутые в функции __init__, неверны. Вместо (я, сообщение, ошибка) это (я, выражение, сообщение). Выражение атрибута - это входное выражение, в котором произошла ошибка, а сообщение - объяснение ошибки.

asthasr
22 апреля 2020 в 21:15
5

Это недоразумение, @ddleon. Пример в документации, на которую вы ссылаетесь, предназначен для конкретного варианта использования. Имя аргументов конструктора подкласса (ни их количество) не имеет значения.

avatar
Jirayu Kaewprateep
17 февраля 2022 в 14:55
-1

Для меня это просто __init__ и переменные, но время от времени проверяется.

Мой образец:

Error_codes = { 100: "Not enough parameters", 101: "Number of special characters more than limits", 102: "At least 18 alphanumeric characters and list of special chars !@#$&*" }

class localbreak( Exception ) :
    Message = ""
    
    def __init__(self, Message):
        self.Message = Message
        return
    def __str__(self):
        print(self.Message)
        return "False"

### When calling ...
raise localbreak(Error_codes[102])

Вывод:

Traceback (most recent call last):   File "ASCII.py", line 150, in <module>
    main(OldPassword, Newpassword)   File "ASCII.py", line 39, in main
    result = read_input("1", "2", Newpassword, "4")                                     
    File "ASCII.py", line 69, in read_input
    raise localbreak(Error_codes[102]) At least 18 alphanumeric characters and list of special chars !@#$&*
__main__.localbreak: False
avatar
Francis Cagney
13 декабря 2021 в 20:32
0

Я наткнулся на эту тему. Вот как я делаю пользовательские исключения. Хотя класс Fault несколько сложен, он упрощает объявление настраиваемых выразительных исключений с переменными аргументами.

FinalViolation, SingletonViolation являются подклассами TypeError, поэтому код будет перехвачен ниже.

try:
    <do something>
except TypeError as ex:
    <handler>

Вот почему Fault не наследуется от Exception. Чтобы разрешить производным исключениям наследовать исключения по своему выбору.

class Fault:
    """Generic Exception base class. Note not descendant of Exception
Inheriting exceptions override formats"""
    formats = '' # to be overriden in descendant classes

    def __init__(self, *args):
        """Just save args for __str__"""
        self.args = args

    def __str__(self):
        """Use formats declared in descendant classes, and saved args to build exception text"""
        return self.formats.format(*self.args)

class TypeFault(Fault, TypeError):
    """Helper class mixing Fault and TypeError"""

class FinalViolation(TypeFault):
    """Custom exception raised if inheriting from 'final' class"""
    formats = "type {} is not an acceptable base type. It cannot be inherited from."

class SingletonViolation(TypeFault):     
    """Custom exception raised if instancing 'singleton' class a second time"""
    formats = "type {} is a singleton. It can only be instanced once."

FinalViolation, SingletonViolation, к сожалению, принимает только 1 аргумент.

Но можно легко создать ошибку с несколькими аргументами, например,

class VesselLoadingError(Fault, BufferError):
    formats = "My {} is full of {}."

raise VesselLoadingError('hovercraft', 'eels')

__main__.VesselLoadingError: My hovercraft is full of eels.

avatar
SeekNDstroy
13 октября 2021 в 15:11
2

У меня были проблемы с вышеуказанными методами, начиная с Python 3.9.5. Однако я обнаружил, что это работает для меня:

class MyException(Exception):
    """Port Exception"""

И затем его можно использовать в коде, например:

try:
    raise MyException('Message')

except MyException as err:
    print (err)
avatar
Macintosh Fan
2 июля 2020 в 20:37
4

Действительно простой подход:

class CustomError(Exception):
    pass

raise CustomError("Hmm, seems like this was custom coded...")

Или вызовите ошибку без вывода __main__ (может выглядеть чище и аккуратнее):

class CustomError(Exception):
    __module__ = Exception.__module__

raise CustomError("Improved CustomError!")
avatar
Eugene Yarmash
29 февраля 2020 в 13:02
23

Чтобы правильно определить собственные исключения, вы должны следовать нескольким рекомендациям:

  • Определите базовый класс , унаследованный от Exception. Это позволит легко отлавливать любые исключения, связанные с проектом:

    class MyProjectError(Exception):
        """A base class for MyProject exceptions."""
    

    Организация классов исключений в отдельном модуле (например, exceptions.py), как правило, является хорошей идеей.

  • Чтобы создать конкретное исключение, создайте подкласс базового класса исключения.

  • Чтобы добавить поддержку дополнительных аргументов в настраиваемое исключение, определите настраиваемый метод __init__() с переменным количеством аргументов. Вызовите __init__() базового класса, передав ему любые позиционные аргументы (помните, что BaseException/Exception ожидайте любое количество позиционных аргументов ): <28575692428>

    class CustomError(MyProjectError):
        def __init__(self, *args, **kwargs):
            super().__init__(*args)
            self.foo = kwargs.get('foo')
    

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

     raise CustomError('Something bad happened', foo='foo')
    

Этот дизайн соответствует принципу замены Лискова, поскольку вы можете заменить экземпляр базового класса исключений экземпляром производного класса исключений. Кроме того, он позволяет создавать экземпляр производного класса с теми же параметрами, что и родительский.

Luke Savefrogs
14 июля 2020 в 05:23
3

Мне действительно нравится этот дизайн ... Мне кажется, он намного чище, чем в других ответах.

loutre
17 июля 2020 в 22:19
0

Адгезия LSP должна быть обязательной, поэтому я предпочитаю этот ответ другим.

james
18 декабря 2020 в 10:33
0

Как проверить, выбрано ли это исключение или нет, с помощью модульных тестов?

ingyhere
29 декабря 2020 в 16:35
0

Было бы это мариновать?

avatar
Yaroslav Nikitenko
10 июня 2019 в 20:40
11

См. Очень хорошую статью «Полное руководство по исключениям Python». Основные принципы:

  • Всегда наследовать от (как минимум) исключения.
  • Всегда вызывайте BaseException.__init__ только с одним аргументом.
  • При построении библиотеки определите базовый класс, унаследованный от Exception.
  • Сообщите подробности об ошибке.
  • Наследовать от встроенных типов исключений, когда это имеет смысл.

Также есть информация об организации (в модулях) и упаковке исключений, рекомендую прочитать руководство.

logicOnAbstractions
5 октября 2019 в 18:09
1

Это хороший пример того, почему в SO я обычно проверяю ответы, получившие наибольшее количество голосов, но также и самые свежие. Полезное дополнение, спасибо.

Eugene Yarmash
29 февраля 2020 в 13:12
4

Always call BaseException.__init__ with only one argument. Похоже на ненужное ограничение, поскольку фактически принимает любое количество аргументов.

Yaroslav Nikitenko
29 февраля 2020 в 13:56
0

@EugeneYarmash Согласен, теперь не понимаю. Я все равно им не пользуюсь. Может стоит перечитать статью и расширить свой ответ.

Yaroslav Nikitenko
3 марта 2020 в 16:51
0

@EugeneYarmash Прочитал статью еще раз. Утверждается, что в случае нескольких аргументов реализация C вызывает "return PyObject_Str (self-> args);" Это значит, что одна струна должна работать лучше, чем несколько. Вы это проверили?

avatar
fameman
25 ноября 2018 в 17:14
49

Начиная с Python 3.8 (2018, https://docs.python.org/dev/whatsnew/3.8.html), рекомендуемый метод по-прежнему: <18375>2798423

class CustomExceptionName(Exception):
    """Exception raised when very uncommon things happen"""
    pass

Пожалуйста, не забудьте задокументировать, почему необходимо настраиваемое исключение!

Если вам нужно, это способ сделать исключения с дополнительными данными:

class CustomExceptionName(Exception):
    """Still an exception raised when uncommon things happen"""
    def __init__(self, message, payload=None):
        self.message = message
        self.payload = payload # you could add more args
    def __str__(self):
        return str(self.message) # __str__() obviously expects a string to be returned, so make sure not to send any other data types

и загрузите их, например:

try:
    raise CustomExceptionName("Very bad mistake.", "Forgot upgrading from Python 1")
except CustomExceptionName as error:
    print(str(error)) # Very bad mistake
    print("Detail: {}".format(error.payload)) # Detail: Forgot upgrading from Python 1

payload=None важно для маринования. Перед тем, как сбросить его, необходимо позвонить по телефону error.__reduce__(). Загрузка будет работать должным образом.

Возможно, вам стоит изучить поиск решения с помощью оператора pythons return, если вам нужно передать много данных во внешнюю структуру. Мне это кажется более ясным / питоническим. Расширенные исключения широко используются в Java, что иногда может раздражать при использовании фреймворка и необходимости отлавливать все возможные ошибки.

kevlarr
26 марта 2019 в 16:46
3

По крайней мере, текущие документы указывают, что это способ сделать это (по крайней мере, без __str__), а не другие ответы, в которых используется super().__init__(...) .. Просто позор, который отменяет для __str__ и __repr__, вероятно, необходимы просто для лучшей сериализации "по умолчанию".

Roel Schroeven
6 августа 2019 в 15:21
5

Честный вопрос: почему так важно, чтобы исключения были маринованными? Каковы варианты использования выгрузки и загрузки исключений?

logicOnAbstractions
6 октября 2019 в 02:58
2

@RoelSchroeven: Мне однажды пришлось распараллеливать код. Выполняется нормально для отдельного процесса, но некоторые из его классов не могут быть сериализуемы (лямбда-функция передается как объекты). Мне потребовалось некоторое время, чтобы разобраться и исправить. Это означает, что кому-то позже может потребоваться сериализация вашего кода, он не сможет этого сделать и должен выяснить, почему ... Моя проблема не заключалась в невыявленных ошибках, но я вижу, что это вызывает аналогичные проблемы.

avatar
omkaartg
22 июля 2018 в 05:27
4

Попробуйте этот пример

class InvalidInputError(Exception):
    def __init__(self, msg):
        self.msg = msg
    def __str__(self):
        return repr(self.msg)

inp = int(input("Enter a number between 1 to 10:"))
try:
    if type(inp) != int or inp not in list(range(1,11)):
        raise InvalidInputError
except InvalidInputError:
    print("Invalid input entered")
avatar
Aaron Hall
14 ноября 2014 в 21:09
228

«Как правильно объявлять пользовательские исключения в современном Python?»

Это нормально, если ваше исключение действительно не является типом более конкретного исключения:

class MyException(Exception):
    pass

Или лучше (возможно, идеально) вместо pass укажите строку документации:

class MyException(Exception):
    """Raise for my specific kind of exception"""

Создание подклассов исключений

Из docs

Exception

Все встроенные исключения, не связанные с выходом из системы, являются производными от этого класса. Все пользовательские исключения также должны быть производными от этого класс.

Это означает, что , если ваше исключение является типом более конкретного исключения, подклассифицируйте это исключение вместо общего Exception (и в результате вы по-прежнему будете производным от Exception как документы рекомендуют). Кроме того, вы можете, по крайней мере, предоставить строку документации (и не использовать ключевое слово pass):

class MyAppValueError(ValueError):
    '''Raise when my specific value is wrong'''

Задайте атрибуты, которые вы создаете самостоятельно, с помощью пользовательского __init__. Не передавайте dict в качестве позиционного аргумента, будущие пользователи вашего кода будут вам благодарны. Если вы используете устаревший атрибут сообщения, назначив его самостоятельно, вы избежите DeprecationWarning:

class MyAppValueError(ValueError):
    '''Raise when a specific subset of values in context of app is wrong'''
    def __init__(self, message, foo, *args):
        self.message = message # without this you may get DeprecationWarning
        # Special attribute you desire with your Error, 
        # perhaps the value that caused the error?:
        self.foo = foo         
        # allow users initialize misc. arguments as any other builtin Error
        super(MyAppValueError, self).__init__(message, foo, *args) 

На самом деле нет необходимости писать свои собственные __str__ или __repr__. Встроенные очень хороши, и ваше совместное наследование гарантирует, что вы их используете.

Критика верхнего ответа

Может я пропустил вопрос, но почему бы и нет:

class MyException(Exception):
    pass

Опять же, проблема с вышеизложенным состоит в том, что для его обнаружения вам нужно либо указать его конкретное имя (импортировать, если оно создано в другом месте), либо перехватить Exception (но вы, вероятно, не готовы обрабатывать все типы of Exceptions, и вам следует перехватывать только те исключения, которые вы готовы обработать). Подобная критика приведена ниже, но, кроме того, это не способ инициализации с помощью super, и вы получите DeprecationWarning, если получите доступ к атрибуту сообщения:

Изменить: чтобы что-то переопределить (или передать дополнительные аргументы), сделайте следующее:

class ValidationError(Exception):
    def __init__(self, message, errors):

        # Call the base class constructor with the parameters it needs
        super(ValidationError, self).__init__(message)

        # Now for your custom code...
        self.errors = errors

Таким образом вы можете передать dict сообщений об ошибках второму параметру и получить к нему позже с помощью e.errors

Также требуется передать ровно два аргумента (кроме self.) Ни больше, ни меньше. Это интересное ограничение, которое будущие пользователи могут не оценить.

Если говорить прямо - это нарушает заменяемость Лискова.

Я продемонстрирую обе ошибки:

>>> ValidationError('foo', 'bar', 'baz').message

Traceback (most recent call last):
  File "<pyshell#10>", line 1, in <module>
    ValidationError('foo', 'bar', 'baz').message
TypeError: __init__() takes exactly 3 arguments (4 given)

>>> ValidationError('foo', 'bar').message
__main__:1: DeprecationWarning: BaseException.message has been deprecated as of Python 2.6
'foo'

По сравнению с:

>>> MyAppValueError('foo', 'FOO', 'bar').message
'foo'
Kos
3 января 2018 в 18:21
3

Привет с 2018 года! BaseException.message отсутствует в Python 3, поэтому критика относится только к старым версиям, верно?

Aaron Hall♦
3 января 2018 в 19:02
8

@Kos Критика о заменяемости Лискова все еще в силе. Семантика первого аргумента как «сообщения» также, возможно, сомнительна, но я не думаю, что буду спорить по этому поводу. Я посмотрю на это побольше, когда у меня будет больше свободного времени.

Jacquot
18 марта 2018 в 23:39
1

FWIW, для Python 3 (по крайней мере, для 3.6+), можно было бы переопределить метод __str__ для MyAppValueError вместо того, чтобы полагаться на атрибут message

cowbert
14 июля 2018 в 03:18
1

зачем избегать использования диктовки в качестве позиционного аргумента? Он сохраняет всю исходную семантику, включая (__repr__ / __str__), и пользователь может просто проанализировать dict через .args[0] на ответ frnknstn? (Вы отмечаете это в строке документации, не так ли?)

ostergaard
16 сентября 2018 в 13:52
1

@AaronHall Не могли бы вы подробнее рассказать о преимуществах подкласса ValueError, а не Exception? Вы заявляете, что это то, что подразумевается в документации, но прямое чтение не поддерживает такую ​​интерпретацию, и в Руководстве по Python в разделе Пользовательские исключения явно делает это выбором пользователя: «Исключения обычно должны быть производными от класса Exception, прямо или косвенно ". Поэтому мы хотим понять, оправдана ли ваша точка зрения, пожалуйста.

Aaron Hall♦
16 сентября 2018 в 21:17
2

@ostergaard Не могу ответить полностью прямо сейчас, но, короче говоря, пользователь получает дополнительную возможность поймать ValueError. Это имеет смысл, если он относится к категории ошибок значений. Если он не относится к категории ошибок значений, я бы возразил против его семантики. Со стороны программиста есть место для некоторых нюансов и рассуждений, но я предпочитаю конкретику, когда это возможно. Я обновлю свой ответ, чтобы лучше заняться этой темой в ближайшее время.

ostergaard
28 сентября 2018 в 13:12
0

@AaronHall, спасибо, это имеет смысл, и я согласен. Хотя я считаю, что приписывать это значение документам - преувеличение.

Eugene Yarmash
28 февраля 2020 в 15:34
2

Не вижу особого смысла в соблюдении принципа подстановки Лискова с пользовательскими исключениями. Вы вызываете конкретное исключение, чтобы указать конкретное условие. Зачем вам когда-либо нужно заменять экземпляр базового класса исключений экземпляром производного класса исключений?

Aaron Hall♦
28 февраля 2020 в 15:51
0

@ Евгений Ярмаш Почему? Если вы вызываете общее исключение, возможно, позже вы захотите поднять подкласс и не мешать пользователям, которые все еще улавливают более общее исключение, тогда вам нужна заменяемость, которая не нарушает работу пользователя.

Eugene Yarmash
28 февраля 2020 в 18:02
2

@AaronHall Мне это никогда не требовалось на практике. В любом случае, я предполагаю, что ваш пример также нарушает LSP: если в вашем коде используется raise ValueError(msg), вы не можете заменить это на raise ValidationError(msg), поскольку последний требует еще один параметр.

Aaron Hall♦
28 февраля 2020 в 18:13
1

@EugeneYarmash Вот интересный момент, который я давно обдумывал! Является ли подкласс Liskov Substitutable (LS), если он запрещает такой же вызов конструктора в дочернем элементе, что и в родительском? Может, это строго не LS. Но в большинстве случаев я предполагаю, что это никого не должно сломать. Однако я полагаю, что это возможно, если вы ловите и создаете экземпляр type(error) вместо класса по имени. Можете ли вы найти мне источник, чтобы процитировать это? (Любая рецензируемая статья, в которой обсуждается это, предпочтительнее не делать.)

Anakhand
29 июня 2020 в 14:39
2

В связи с тем, что сказал @Eugene, применяется ли принцип подстановки Лискова к конструкторам? (См. Также: это, это.) В частности, в случае исключений вполне может быть, что я решу заменить общее выражение более конкретным, но в В этом случае я также позабочусь предоставить необходимые аргументы - в противном случае это полусырое дело.

avatar
mykhal
7 августа 2013 в 16:23
55

посмотрите, как работают исключения по умолчанию, если используется один против дополнительных атрибутов (трассировки опущены):

>>> raise Exception('bad thing happened')
Exception: bad thing happened

>>> raise Exception('bad thing happened', 'code is broken')
Exception: ('bad thing happened', 'code is broken')

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

>>> nastyerr = NastyError('bad thing happened')
>>> raise nastyerr
NastyError: bad thing happened

>>> raise nastyerr()
NastyError: bad thing happened

>>> raise nastyerr('code is broken')
NastyError: ('bad thing happened', 'code is broken')

это легко сделать с этим подклассом

class ExceptionTemplate(Exception):
    def __call__(self, *args):
        return self.__class__(*(self.args + args))
# ...
class NastyError(ExceptionTemplate): pass

, и если вам не нравится это представление, подобное кортежу по умолчанию, просто добавьте метод __str__ в класс ExceptionTemplate, например:

    # ...
    def __str__(self):
        return ': '.join(self.args)

и у вас будет

>>> raise nastyerr('code is broken')
NastyError: bad thing happened: code is broken
avatar
frnknstn
22 апреля 2012 в 18:18
574

С современными исключениями Python вам не нужно злоупотреблять .message, переопределять .__str__() или .__repr__() или что-либо из этого. Если все, что вам нужно, это информативное сообщение при возникновении исключения, сделайте следующее:

class MyException(Exception):
    pass

raise MyException("My hovercraft is full of eels")

Это даст обратную трассировку, заканчивающуюся на MyException: My hovercraft is full of eels.

Если вы хотите большей гибкости от исключения, вы можете передать словарь в качестве аргумента:

raise MyException({"message":"My hovercraft is full of animals", "animal":"eels"})

Однако получить эти детали в блоке except немного сложнее. Подробности хранятся в атрибуте args, который представляет собой список. Вам нужно будет сделать что-то вроде этого:

try:
    raise MyException({"message":"My hovercraft is full of animals", "animal":"eels"})
except MyException as e:
    details = e.args[0]
    print(details["animal"])

По-прежнему можно передать несколько элементов в исключение и получить к ним доступ через индексы кортежей, но это крайне обескураживает (и некоторое время назад даже планировалось исключить). Если вам нужно больше, чем единичная информация, и описанного выше метода вам недостаточно, тогда вам следует создать подкласс Exception, как описано в учебнике.

.
class MyError(Exception):
    def __init__(self, message, animal):
        self.message = message
        self.animal = animal
    def __str__(self):
        return self.message
mtraceur
20 апреля 2018 в 22:36
2

"но в будущем это будет нерекомендуемо" - это все еще предназначено для прекращения поддержки? Python 3.7 по-прежнему с радостью принимает Exception(foo, bar, qux).

frnknstn
2 мая 2018 в 08:20
0

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

neves
8 мая 2018 в 17:48
0

@frnknstn, почему это не рекомендуется? Похоже, мне нравится идиома.

frnknstn
10 мая 2018 в 08:09
3

Для начала @neves, использование кортежей для хранения информации об исключениях не имеет преимуществ перед использованием словаря для того же. Если вас интересует причина изменений исключения, взгляните на PEP352

liberforce
17 апреля 2019 в 16:25
3

Соответствующий раздел PEP352 - «Отказанные идеи».

avatar
Lennart Regebro
23 августа 2009 в 21:58
9

Нет, «сообщение» не запрещено. Это просто устарело. Ваше приложение будет нормально работать с использованием сообщения. Но вы, конечно, можете избавиться от ошибки устаревания.

Когда вы создаете настраиваемые классы Exception для своего приложения, многие из них являются подклассами не только от Exception, но и от других, например ValueError или аналогичных. Затем вам нужно приспособиться к их использованию переменных.

И если в вашем приложении много исключений, обычно рекомендуется иметь общий настраиваемый базовый класс для всех из них, чтобы пользователи ваших модулей могли выполнять

try:
    ...
except NelsonsExceptions:
    ...

И в этом случае вы можете выполнить необходимое здесь __init__ and __str__, поэтому вам не нужно повторять это для каждого исключения. Но просто вызов переменной сообщения чем-то еще, кроме message, дает трюк.

В любом случае, __init__ or __str__ вам нужен только в том случае, если вы делаете что-то отличное от того, что делает само Exception. И потому, что если устаревание, то вам понадобятся оба, иначе вы получите ошибку. Это не так уж много дополнительного кода, необходимого для каждого класса. ;)

Yaroslav Nikitenko
10 июня 2019 в 20:05
1

Интересно, что исключения Django не наследуются от общей базы. docs.djangoproject.com/en/2.2/_modules/django/core/exceptions У вас есть хороший пример, когда требуется перехват всех исключений из определенного приложения? (возможно, это полезно только для некоторых конкретных типов приложений).

Yaroslav Nikitenko
10 июня 2019 в 20:44
1

Я нашел хорошую статью по этой теме, julien.danjou.info/python-exceptions-guide. Я думаю, что исключения следует разделять на подклассы в первую очередь на основе домена, а не приложения. Когда ваше приложение связано с протоколом HTTP, вы наследуете HTTPError. Когда частью вашего приложения является TCP, вы получаете исключения этой части из TCPError. Но если ваше приложение охватывает много доменов (файлов, разрешений и т. Д.), Причина для MyBaseException уменьшается. Или для защиты от «послойного нарушения»?

avatar
M. Utku ALTINKAYA
23 августа 2009 в 21:46
18

Вы должны переопределить методы __repr__ или __unicode__ вместо использования message, аргументы, которые вы предоставляете при создании исключения, будут в атрибуте args объекта исключения.