Ручное создание (выброс) исключения в Python

avatar
TIMEX
12 января 2010 в 21:07
2112103
12
2627

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

Источник

Ответы (12)

avatar
Aaron Hall
5 июня 2014 в 16:30
3462

Как вручную вызвать / вызвать исключение в 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 инструкция

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

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.')
Sharath BJ
15 января 2021 в 11:49
0

попробуйте: поднять ValueError ('error') за исключением ValueError как e: print ('мы поймем исключение: Exception', str (e)) # это лучшее решение

Aaron Hall♦
15 января 2021 в 16:18
0

@SharathBJ вы поднимаете ValueError и указываете его как тип Exception, и это ненужная потеря точности. repr(e) как минимум сообщит вам о типе.

Gabriel Staples
5 февраля 2021 в 19:50
0

Где официальная документация, показывающая, что вы можете передать сообщение при создании исключения? например: raise TypeError("my message")

Aaron Hall♦
5 февраля 2021 в 21:56
1

@GabrielStaples вот документы по аргументам создания экземпляров: docs.python.org/3/library/exceptions.html#BaseException.args

Gabriel Staples
5 февраля 2021 в 23:34
0

@AaronHall, а, понятно. Я не понимал, что тип исключения, такой как TypeError, был вызовом конструктора класса! Теперь это имеет смысл. Таким образом, сообщение является общим аргументом конструктора для классов ошибок.

avatar
error 1044
8 декабря 2021 в 16:35
1

если вас не волнует возникшее исключение, выполните:

def crash(): return 0/0

the good old division be 0

avatar
U12-Forward
12 сентября 2021 в 01:59
1

Если вас не волнует какая ошибка возникает, вы можете использовать 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, и это поднимает его с этим текстом.

avatar
Sathia
25 августа 2021 в 17:38
6

Чтобы перехватить все исключения, используйте 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

avatar
LuisAFK
8 июня 2021 в 16:20
1

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

Вы можете добиться этого следующим образом:

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 и т. Д.

avatar
Abhijeet.py
31 марта 2020 в 08:37
1

Для этого вам следует выучить оператор Raise в Python. Он должен храниться внутри блока try. Пример -

try:
    raise TypeError            #remove TypeError by any other error if you want
except TypeError:
    print('TypeError raised')
Brian
21 октября 2020 в 14:26
1

Можете ли вы объяснить, почему ваш пример хорош? Я прочитал все ответы на этот вопрос, и мне искренне любопытно.

avatar
Rehan Haider
27 февраля 2019 в 09:42
5

Другой способ создания исключений - 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))
cowbert
24 сентября 2020 в 16:47
1

не надежный, поскольку утверждения в CPython игнорируются, когда интерпретатор инициализируется с флагом оптимизации (-O); если вы хотите действительно контролировать выполнение программы "эй, это условие не должно произойти, но прервите работу, если оно истинно", вручную raise AssertionError()

avatar
markemus
3 декабря 2018 в 16:13
6

Обратите внимание: бывают случаи, когда вам НЕОБХОДИМО обрабатывать общие исключения. Если вы обрабатываете кучу файлов и регистрируете свои ошибки, вы можете захотеть перехватить любую ошибку, которая возникает для файла, зарегистрировать ее и продолжить обработку остальных файлов. В этом случае

try:
    foo() 
except Exception as e:
    print(e) # Print out handled error

блок - хороший способ сделать это. Вы все равно захотите raise определенных исключений, чтобы вы знали, что они означают.

avatar
Anant Prakash
29 марта 2017 в 11:59
14

Сначала прочтите существующие ответы, это всего лишь дополнение.

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

Пример:

raise SystemExit

закрывает программу, но вы можете захотеть узнать, что произошло. Так что вы можете использовать это.

raise SystemExit("program exited")

перед закрытием программы в stderr будет выведено сообщение «программа завершена».

burny
1 октября 2019 в 08:28
3

Разве это не противоречит парадигме ООП? Я предполагаю, что в первом случае создается ссылка на класс, а во втором - экземпляр SystemExit. Разве raise SystemExit() не был бы лучшим выбором? Почему первый вообще работает?

ptim
9 августа 2021 в 01:01
0

python.org/dev/peps/pep-3109 обсуждает это

avatar
N Randhawa
8 ноября 2016 в 17:54
71

В 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
Gloweye
28 июня 2019 в 07:15
13

Обратите внимание, что PEP8 предпочитает exception(args), а не exception (args)

cg909
4 апреля 2020 в 17:06
4

Также имеется raise exception(args) from None, чтобы сказать, что текущее активное исключение было обработано и больше не представляет интереса. В противном случае, если вы вызываете исключение внутри блока except, и оно не обрабатывается, трассировки для обоих исключений будут показаны разделенными сообщением «Во время обработки указанного выше исключения произошло другое исключение».

avatar
Evgeni Sergeev
19 мая 2015 в 04:55
39

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

if 0 < distance <= RADIUS:
    #Do something.
elif RADIUS < distance:
    #Do something.
else:
    raise AssertionError("Unexpected value of 'distance'!", distance)
Two-Bit Alchemist
16 сентября 2015 в 21:33
23

Это лучший случай для ValueError, чем для AssertionError, потому что нет проблем с утверждением (потому что здесь ничего не делается) - проблема связана со значением. Если в этом случае вам действительно нужен AssertionError, напишите assert distance > 0, 'Distance must be positive'. Но вы не должны проверять ошибки таким образом, потому что утверждения можно отключить (python -O).

Evgeni Sergeev
17 сентября 2015 в 01:31
1

@ Two-BitAlchemist Хорошее замечание. Идея упростилась, когда я написал простой пример выше. Во многих подобных случаях это условие, не связанное с конкретным значением. Скорее, это означает «поток управления никогда не должен попадать сюда».

Evgeni Sergeev
17 сентября 2015 в 01:31
2

@ Two-BitAlchemist Assertions можно отключить, да, но тогда вы вообще не должны использовать их для проверки ошибок?

Two-Bit Alchemist
17 сентября 2015 в 17:05
0

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

Evgeni Sergeev
21 сентября 2015 в 02:36
1

@ Two-BitAlchemist Для меня роль утверждений заключается не в проверке ошибок как таковой (для чего и предназначено тестирование), а в том, что они устанавливают ограждения внутри кода, через которые не могут пройти определенные ошибки. Так становится легче отслеживать и изолировать ошибки, которые неизбежно возникнут. Это просто хорошие привычки, которые требуют небольших усилий, а тестирование требует больших усилий и много времени.

Jonathan Hartley
29 сентября 2015 в 13:33
0

«тестирование требует много усилий и много времени» - правда, но не столько, сколько времени и усилий, которые оно экономит.

Daerdemandt
12 сентября 2016 в 18:05
0

Ошибка может быть воспроизведена, если псевдоконстантный RADIUS также каким-то образом установлен отрицательным.

avatar
Gabriel Hurley
12 января 2010 в 21:08
528

НЕ ДЕЛАЙТЕ ЭТО . Поднимать голый Exception абсолютно не правильно; см. вместо этого отличный ответ Аарона Холла.

Ничего более питонического, чем это:

raise Exception("I know python!")

Если вам нужна дополнительная информация, см. документацию об операторе повышения для Python.

Dawood ibn Kareem
21 января 2015 в 22:23
88

Нет пожалуйста! Это исключает возможность конкретизировать то, что вы поймаете. Это ПОЛНОСТЬЮ неправильный способ сделать это. Взгляните на отличный ответ Аарона Холла вместо этого. В такие времена я хотел бы дать более одного голоса против за каждый ответ.

Dawood ibn Kareem
16 февраля 2015 в 09:38
37

@PeterR Столь же ужасно, что у него так мало голосов против. ЛЮБОМУ, читающему этот ответ, НЕ ДЕЛАЙТЕ ЭТО НИКОГДА! Правильный ответ - это Аарон Холл.

Charlie Parker
22 октября 2016 в 00:07
8

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

Dinei
24 февраля 2017 в 12:42
14

@CharlieParker Есть. Это первая часть ответа Аарона Холла.

codeforester
20 октября 2017 в 18:45
7

Почему этот ответ нельзя пометить для удаления? Он уже получил 93 голоса против!

Daniel F.
13 апреля 2018 в 07:09
6

@codeforester, возможно, потому, что отвечает на вопрос.

ctpenrose
19 октября 2018 в 23:50
7

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

pooya13
9 марта 2021 в 04:39
0

Это Python. Часто вы пишете сценарий из 20 строк для некоторой быстрой работы, которая может пойти не так, как надо, и создание общего исключения с информативным сообщением - лучший инструмент для этой работы.

alper
19 мая 2021 в 15:20
0

Когда я делаю только raise Exception("I know python!"), я не могу распечатать его след

Lucas Basquerotto
16 августа 2021 в 23:12
0

Я согласен с @ctpenrose здесь. В большинстве случаев, когда вы перехватываете исключение, уже слишком поздно, чтобы восстановить его должным образом (если вы не поймаете его очень близко от того места, где оно действительно произошло). В большинстве случаев вам действительно нужно подробное описание ошибки. Здесь подойдет общее исключение (или какое-то специальное исключение для тех случаев, когда вы просто хотите показать сообщение с трассировкой стека). Редко я ловлю исключение, чтобы просто регистрировать и ничего не делать, как в случае, когда Аарон опубликовал как плохую практику Exception (и в этих случаях я, вероятно, хочу перехватить все исключения).