пользовательские - проверка на ошибку python




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

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

Под «современным Python» я подразумеваю что-то, что будет работать в Python 2.5, но «правильно» для Python 2.6 и Python 3. *. И под «custom» я имею в виду объект 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 параметров, но я никогда не знал, как его использовать. Я также не уверен, что это правильный способ сделать что-то в будущем; много обсуждений, которые я нашел в Интернете, предположили, что они пытались покончить с args в Python 3.

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


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

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

class MyException(Exception):
    pass

Или лучше (может быть, идеально), вместо pass дайте docstring:

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

Подклассы исключений подкласса

Из docs

Exception

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

Это означает, что если ваше исключение является типом более конкретного исключения, подкласс, это исключение вместо общего Exception (и результат будет заключаться в том, что вы все еще получаете Exception как рекомендуется документами). Кроме того, вы можете, по крайней мере, предоставить docstring (и не будете вынуждены использовать ключевое слово 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

Опять же, проблема с вышесказанным заключается в том, что для того, чтобы поймать его, вам придется либо называть его конкретно (импортировать его, если он создан в другом месте), либо перехватывать исключение (но вы, вероятно, не готовы обрабатывать все типы исключений, и вы должны только поймать исключения, которые вы готовы обработать). Подобная критика ниже, но, кроме того, это не способ инициализации через 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'

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

class MyException(Exception):
    pass

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

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

Обновление Python 3: в Python 3+ вы можете использовать это несколько более компактное использование super() :

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

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

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

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

try:
    ...
except NelsonsExceptions:
    ...

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

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


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

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")

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

>>> 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






exception