Как я могу вызвать исключение в Python, чтобы позже его можно было перехватить с помощью блока except
?
Ручное создание (выброс) исключения в Python
Ответы (12)
Как вручную вызвать / вызвать исключение в 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
был добавлен к BaseException
в пользу поощрения пользователей к подклассу Исключений и прекращению использования args
, но введение message
и первоначальное устаревание аргументов отозван.
Рекомендации: except
пункт
Находясь внутри предложения except, вы можете, например, записать в журнал, что произошла ошибка определенного типа, а затем повторно вызвать. Лучший способ сделать это при сохранении трассировки стека - использовать оператор простого повышения. Например:
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 , предпочитайте использовать пустой 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]
Если хотите, вы можете изменить то, что происходит с вашим новым повышением - например, установка нового 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.')
если вас не волнует возникшее исключение, выполните:
def crash(): return 0/0
the good old division be 0
Если вас не волнует какая ошибка возникает, вы можете использовать assert
, чтобы вызвать AssertionError
:
>>> assert False, "Manually raised error"
Traceback (most recent call last):
File "<pyshell#24>", line 1, in <module>
assert False, "Manually raised error"
AssertionError: Manually raised error
>>>
Ключевое слово assert
вызывает AssertionError
, если условие False
, в этом случае мы указали False
напрямую, поэтому оно вызывает ошибку, но чтобы оно имело текст, который мы хотим, чтобы оно поднималось, мы добавляем запятую и указываем текст ошибки, который мы хотим, в этом случае я написал Manually raised error
, и это поднимает его с этим текстом.
Чтобы перехватить все исключения, используйте BaseException
, он находится на вершине иерархии исключений.
#1 Перехват исключения
try:
#Do something
except BaseException as error:
print('An exception occurred: {}'.format(error))
#2 Поднять исключение
try:
#Do something
except BaseException as error:
raise 'An exception occurred: {}'.format(error)
Приведенный выше код поддерживает Python 2.7 до последней версии.
Ссылка: https://docs.python.org/3.9/library/exceptions.html#exception-hierarchy
Вы также можете создать настраиваемые исключения . Например, если вы пишете библиотеку, очень полезно создать базовый класс исключения для вашего модуля, а затем иметь собственные под-исключения, чтобы быть более конкретными.
Вы можете добиться этого следующим образом:
class MyModuleBaseClass(Exception):
pass
class MoreSpecificException(MyModuleBaseClass):
pass
# To raise custom exceptions, you can just
# use the raise keyword
raise MoreSpecificException
raise MoreSpecificException('message')
Если вас не интересует настраиваемый базовый класс, вы можете просто унаследовать свои настраиваемые классы исключений от обычного класса исключений, например Exception
, TypeError
, ValueError
и т. Д.
Для этого вам следует выучить оператор Raise в Python. Он должен храниться внутри блока try. Пример -
try:
raise TypeError #remove TypeError by any other error if you want
except TypeError:
print('TypeError raised')
Можете ли вы объяснить, почему ваш пример хорош? Я прочитал все ответы на этот вопрос, и мне искренне любопытно.
Другой способ создания исключений - assert
. Вы можете использовать assert для проверки того, что условие выполняется, если нет, тогда оно вызовет AssertionError
. Подробнее см. здесь.
def avg(marks):
assert len(marks) != 0,"List is empty."
return sum(marks)/len(marks)
mark2 = [55,88,78,90,79]
print("Average of mark2:",avg(mark2))
mark1 = []
print("Average of mark1:",avg(mark1))
не надежный, поскольку утверждения в CPython игнорируются, когда интерпретатор инициализируется с флагом оптимизации (-O); если вы хотите действительно контролировать выполнение программы "эй, это условие не должно произойти, но прервите работу, если оно истинно", вручную raise AssertionError()
Обратите внимание: бывают случаи, когда вам НЕОБХОДИМО обрабатывать общие исключения. Если вы обрабатываете кучу файлов и регистрируете свои ошибки, вы можете захотеть перехватить любую ошибку, которая возникает для файла, зарегистрировать ее и продолжить обработку остальных файлов. В этом случае
try:
foo()
except Exception as e:
print(e) # Print out handled error
блок - хороший способ сделать это. Вы все равно захотите raise
определенных исключений, чтобы вы знали, что они означают.
Сначала прочтите существующие ответы, это всего лишь дополнение.
Обратите внимание, что вы можете вызывать исключения с аргументами или без них.
Пример:
raise SystemExit
закрывает программу, но вы можете захотеть узнать, что произошло. Так что вы можете использовать это.
raise SystemExit("program exited")
перед закрытием программы в stderr будет выведено сообщение «программа завершена».
Разве это не противоречит парадигме ООП? Я предполагаю, что в первом случае создается ссылка на класс, а во втором - экземпляр SystemExit. Разве raise SystemExit()
не был бы лучшим выбором? Почему первый вообще работает?
python.org/dev/peps/pep-3109 обсуждает это
В Python3 есть 4 разных синтаксиса для исключения исключений:
1. raise exception
2. raise exception (args)
3. raise
4. raise exception (args) from original_exception
1. поднять исключение против 2. поднять исключение (аргументы)
Если вы используете raise exception (args)
для создания исключения, тогда args
будет напечатан при печати объекта исключения - как показано в примере ниже.
#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 execption
try:
raise ValueError
except ValueError as exp:
print ("Error", exp) # Output -> Error
Оператор3.поднять
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 8, in <module>
#ZeroDivisionError: division by zero
4. возбудить исключение (аргументы) из исходного_исключения
Этот оператор используется для создания цепочки исключений, в которой исключение, которое возникает в ответ на другое исключение, может содержать сведения об исходном исключении, как показано в примере ниже.
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
Обратите внимание, что PEP8 предпочитает exception(args)
, а не exception (args)
Также имеется raise exception(args) from None
, чтобы сказать, что текущее активное исключение было обработано и больше не представляет интереса. В противном случае, если вы вызываете исключение внутри блока except
, и оно не обрабатывается, трассировки для обоих исключений будут показаны разделенными сообщением «Во время обработки указанного выше исключения произошло другое исключение».
Для общего случая, когда вам нужно сгенерировать исключение в ответ на некоторые неожиданные условия и которое вы никогда не собираетесь перехватывать, а просто быстро вывести из строя, чтобы вы могли отлаживать оттуда, если это когда-либо произойдет - самый логичный похоже на AssertionError
:
if 0 < distance <= RADIUS:
#Do something.
elif RADIUS < distance:
#Do something.
else:
raise AssertionError("Unexpected value of 'distance'!", distance)
Это лучший случай для ValueError
, чем для AssertionError
, потому что нет проблем с утверждением (потому что здесь ничего не делается) - проблема связана со значением. Если в этом случае вам действительно нужен AssertionError
, напишите assert distance > 0, 'Distance must be positive'
. Но вы не должны проверять ошибки таким образом, потому что утверждения можно отключить (python -O
).
@ Two-BitAlchemist Хорошее замечание. Идея упростилась, когда я написал простой пример выше. Во многих подобных случаях это условие, не связанное с конкретным значением. Скорее, это означает «поток управления никогда не должен попадать сюда».
@ Two-BitAlchemist Assertions можно отключить, да, но тогда вы вообще не должны использовать их для проверки ошибок?
Смотря как. Я бы не позволил, чтобы это была моя единственная проверка ошибок в программе, которую я намеревался распространять. С другой стороны, я мог бы создать программу только для моих коллег и сказать им, что они используют ее на свой страх и риск, если они запускают ее с -O
.
@ Two-BitAlchemist Для меня роль утверждений заключается не в проверке ошибок как таковой (для чего и предназначено тестирование), а в том, что они устанавливают ограждения внутри кода, через которые не могут пройти определенные ошибки. Так становится легче отслеживать и изолировать ошибки, которые неизбежно возникнут. Это просто хорошие привычки, которые требуют небольших усилий, а тестирование требует больших усилий и много времени.
«тестирование требует много усилий и много времени» - правда, но не столько, сколько времени и усилий, которые оно экономит.
Ошибка может быть воспроизведена, если псевдоконстантный RADIUS также каким-то образом установлен отрицательным.
НЕ ДЕЛАЙТЕ ЭТО . Поднимать голый
Exception
абсолютно не правильно; см. вместо этого отличный ответ Аарона Холла.
Ничего более питонического, чем это:
raise Exception("I know python!")
Если вам нужна дополнительная информация, см. документацию об операторе повышения для Python.
Нет пожалуйста! Это исключает возможность конкретизировать то, что вы поймаете. Это ПОЛНОСТЬЮ неправильный способ сделать это. Взгляните на отличный ответ Аарона Холла вместо этого. В такие времена я хотел бы дать более одного голоса против за каждый ответ.
@PeterR Столь же ужасно, что у него так мало голосов против. ЛЮБОМУ, читающему этот ответ, НЕ ДЕЛАЙТЕ ЭТО НИКОГДА! Правильный ответ - это Аарон Холл.
Я думаю, что должно быть более подробное объяснение того, почему это неправильно или так плохо.
@CharlieParker Есть. Это первая часть ответа Аарона Холла.
Почему этот ответ нельзя пометить для удаления? Он уже получил 93 голоса против!
@codeforester, возможно, потому, что отвечает на вопрос.
Этот ответ все еще здесь, потому что многие опытные разработчики не согласны с использованием специализированных классов исключений. Хотел бы я отрицать комментарии.
Это Python. Часто вы пишете сценарий из 20 строк для некоторой быстрой работы, которая может пойти не так, как надо, и создание общего исключения с информативным сообщением - лучший инструмент для этой работы.
Когда я делаю только raise Exception("I know python!")
, я не могу распечатать его след
Я согласен с @ctpenrose здесь. В большинстве случаев, когда вы перехватываете исключение, уже слишком поздно, чтобы восстановить его должным образом (если вы не поймаете его очень близко от того места, где оно действительно произошло). В большинстве случаев вам действительно нужно подробное описание ошибки. Здесь подойдет общее исключение (или какое-то специальное исключение для тех случаев, когда вы просто хотите показать сообщение с трассировкой стека). Редко я ловлю исключение, чтобы просто регистрировать и ничего не делать, как в случае, когда Аарон опубликовал как плохую практику Exception (и в этих случаях я, вероятно, хочу перехватить все исключения).
попробуйте: поднять ValueError ('error') за исключением ValueError как e: print ('мы поймем исключение: Exception', str (e)) # это лучшее решение
@SharathBJ вы поднимаете
ValueError
и указываете его как типException
, и это ненужная потеря точности.repr(e)
как минимум сообщит вам о типе.Где официальная документация, показывающая, что вы можете передать сообщение при создании исключения? например:
raise TypeError("my message")
@GabrielStaples вот документы по аргументам создания экземпляров: docs.python.org/3/library/exceptions.html#BaseException.args
@AaronHall, а, понятно. Я не понимал, что тип исключения, такой как
TypeError
, был вызовом конструктора класса! Теперь это имеет смысл. Таким образом, сообщение является общим аргументом конструктора для классов ошибок.