Вопрос-Ответ

Manually raising (throwing) an exception in Python

Создание исключения вручную в Python

Как мне создать исключение в Python, чтобы позже его можно было перехватить с помощью except блока?

Переведено автоматически
Ответ 1

Как мне вручную создать исключение в Python?


Используйте наиболее специфичный конструктор исключений, который семантически соответствует вашей проблеме.

Будьте конкретны в своем сообщении, например:

raise ValueError('A very specific bad thing happened.')

Не создавайте универсальные исключения

Избегайте создания универсального исключения Exception. Чтобы перехватить его, вам придется перехватывать все другие, более специфические исключения, которые относятся к его подклассу.

Проблема 1: Скрытие ошибок

raise Exception('I know Python!') # Don't! If you catch, likely to hide bugs.

Например:

def demo_bad_catch():
try:
raise ValueError('Represents a hidden bug, do not catch this')
raise Exception('This is the exception you expect to handle')
except Exception as error:
print('Caught this error: ' + repr(error))

>>> demo_bad_catch()
Caught this error: ValueError('Represents a hidden bug, do not catch this',)

Проблема 2: не перехватывается

И более конкретные перехваты не будут перехватывать общее исключение:

def demo_no_catch():
try:
raise Exception('general exceptions not caught by specific handling')
except ValueError as e:
print('we will not catch exception: Exception')


>>> demo_no_catch()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in demo_no_catch
Exception: general exceptions not caught by specific handling

Рекомендации: raise инструкция

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

raise ValueError('A very specific bad thing happened')

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

raise ValueError('A very specific bad thing happened', 'foo', 'bar', 'baz') 

Доступ к этим аргументам осуществляется через args атрибут в Exception объекте. Например:

try:
some_code_that_may_raise_our_value_error()
except ValueError as err:
print(err.args)

С принтами

('message', 'foo', 'bar', 'baz')    

В Python 2.5 в message был добавлен атрибут actual для BaseException поощрения пользователей к подклассу исключений и прекращению использования args, но введение message и первоначальное устаревание аргументов были отменены.

Рекомендации: except предложение

Находясь внутри предложения except , вы можете, например, зарегистрировать, что произошла ошибка определенного типа, а затем повторно создать исключение. Лучший способ сделать это с сохранением трассировки стека - использовать простой оператор raise . Например:

logger = logging.getLogger(__name__)

try:
do_something_in_app_that_breaks_easily()
except AppError as error:
logger.error(error)
raise # just this!
# raise AppError # Don't do this, you'll lose the stack trace!

Не изменяйте свои ошибки... но если вы настаиваете.

Вы можете сохранить трассировку стека (и значение ошибки) с помощью sys.exc_info(), но это более подвержено ошибкам и имеет проблемы с совместимостью между Python 2 и 3, предпочитаю использовать bare raise для повторного создания.

Для пояснения - sys.exc_info() возвращает тип, значение и обратную трассировку.

type, value, traceback = sys.exc_info()

Это синтаксис в Python 2 - обратите внимание, что это несовместимо с Python 3:

raise AppError, error, sys.exc_info()[2] # avoid this.
# Equivalently, as error *is* the second object:
raise sys.exc_info()[0], sys.exc_info()[1], sys.exc_info()[2]

Если вы хотите, вы можете изменить то, что происходит с вашим новым повышением - например, установить new args для экземпляра:

def error():
raise ValueError('oops!')

def catch_error_modify_message():
try:
error()
except ValueError:
error_type, error_instance, traceback = sys.exc_info()
error_instance.args = (error_instance.args[0] + ' <modification>',)
raise error_type, error_instance, traceback

И мы сохранили всю обратную трассировку при изменении аргументов. Обратите внимание, что это не лучшая практика и это недопустимый синтаксис в Python 3 (что значительно усложняет работу по обеспечению совместимости).

>>> catch_error_modify_message()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in catch_error_modify_message
File "<stdin>", line 2, in error
ValueError: oops! <modification>

В Python 3:

raise error.with_traceback(sys.exc_info()[2])

Еще раз: избегайте манипулирования обратными трассировками вручную. Это менее эффективно и более подвержено ошибкам. И если вы используете потоковую обработку и sys.exc_info вы можете даже получить неправильную обратную трассировку (особенно если вы используете обработку исключений для потока управления - чего я лично стараюсь избегать.)

Python 3, цепочка исключений

В Python 3 вы можете создавать цепочки исключений, которые сохраняют обратные трассировки:

raise RuntimeError('specific message') from error

Имейте в виду:


  • это действительно позволяет изменить тип возникшей ошибки, и

  • это не совместимо с Python 2.

Устаревшие методы:

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

Допустимо в Python 2, но не в Python 3 заключается в следующем:

raise ValueError, 'message' # Don't do this, it's deprecated!

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

raise 'message' # really really wrong. don't do this.

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

Пример использования

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

def api_func(foo):
'''foo should be either 'baz' or 'bar'. returns something very useful.'''
if foo not in _ALLOWED_ARGS:
raise ValueError('{foo} wrong, use "baz" or "bar"'.format(foo=repr(foo)))

Создавайте свои собственные типы ошибок по мере необходимости


"Я хочу специально допустить ошибку, чтобы она попала в исключение"


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

class MyAppLookupError(LookupError):
'''raise this when there's a lookup error for my app'''

и использование:

if important_key not in resource_dict and not ok_to_be_missing:
raise MyAppLookupError('resource is missing, and that is not ok.')
Ответ 2

Не делайте этого. Создавать голое исключение Exception абсолютно не правильно; вместо этого смотрите Отличный ответ Аарона Холла.


Это не может быть намного более питоническим, чем это:

raise Exception("I know Python!")

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

Смотрите Документацию по инструкции raise для Python, если хотите получить дополнительную информацию.

Ответ 3

В Python 3 существует четыре различных синтаксиса для создания исключений:


  1. вызвать исключение

  2. создание исключения (args)

  3. поднять

  4. создание исключения (аргументов) из original_exception

1. Создать исключение против 2. создать исключение (аргументы)

Если вы используете raise exception (args) для создания исключения, то args будет напечатано при печати объекта exception - как показано в примере ниже.

# Raise exception (args)
try:
raise ValueError("I have raised an Exception")
except ValueError as exp:
print("Error", exp) # Output -> Error I have raised an Exception


# Raise exception
try:
raise ValueError
except ValueError as exp:
print("Error", exp) # Output -> Error

3. Оператор raise

Оператор raise без каких-либо аргументов повторно вызывает последнее исключение.

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

def somefunction():
print("some cleaning")

a = 10
b = 0
result = None

try:
result = a / b
print(result)

except Exception: # Output ->
somefunction() # Some cleaning
raise # Traceback (most recent call last):
# File "python", line 9, in <module>
# ZeroDivisionError: division by zero

4. Создайте исключение (аргументы) из original_exception

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

class MyCustomException(Exception):
pass

a = 10
b = 0
reuslt = None
try:
try:
result = a / b

except ZeroDivisionError as exp:
print("ZeroDivisionError -- ",exp)
raise MyCustomException("Zero Division ") from exp

except MyCustomException as exp:
print("MyException",exp)
print(exp.__cause__)

Вывод:

ZeroDivisionError --  division by zero
MyException Zero Division
division by zero
Ответ 4

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

if 0 < distance <= RADIUS:
#Do something.
elif RADIUS < distance:
#Do something.
else:
raise AssertionError("Unexpected value of 'distance'!", distance)
2023-04-23 22:11 python