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

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


person TIMEX    schedule 12.01.2010    source источник


Ответы (9)


Как вручную генерировать исключение в 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 к BaseException был добавлен фактический атрибут message, чтобы побудить пользователей создать подкласс Исключений и прекратить использование 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.')
person Aaron Hall    schedule 05.06.2014
comment
попробуйте: поднять ValueError ('error') за исключением ValueError как e: print ('мы поймем исключение: Exception', str (e)) # это лучшее решение - person Sharath BJ; 15.01.2021
comment
@SharathBJ вы поднимаете ValueError и сообщаете об этом как о типе Exception, и это ненужная потеря точности. repr(e) по крайней мере сообщит вам о типе. - person Aaron Hall; 15.01.2021
comment
Где официальная документация, показывающая, что вы можете передать сообщение при создании исключения? пример: raise TypeError("my message") - person Gabriel Staples; 05.02.2021
comment
@GabrielStaples вот документы по аргументам создания экземпляров: docs.python.org/3 /library/exceptions.html#BaseException.args - person Aaron Hall; 06.02.2021
comment
@AaronHall, а, понятно. Я не понимал, что тип исключения, такой как TypeError, был вызовом конструктора класса! Теперь это имеет смысл. Таким образом, сообщение является общим аргументом конструктора для классов ошибок. - person Gabriel Staples; 06.02.2021

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

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

raise Exception("I know python!")

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

person Gabriel Hurley    schedule 12.01.2010
comment
Пожалуйста нет! Это исключает возможность конкретизировать то, что вы поймаете. Это ПОЛНОСТЬЮ неправильный способ сделать это. Взгляните на отличный ответ Аарона Холла вместо этого. В такие времена я хотел бы дать более одного голоса против за каждый ответ. - person Dawood ibn Kareem; 22.01.2015
comment
@PeterR Столь же ужасно, что у него так мало голосов против. ЛЮБОМУ, читающему этот ответ, НЕ ДЕЛАЙТЕ ЭТО НИКОГДА! Правильный ответ - это Аарон Холл. - person Dawood ibn Kareem; 16.02.2015
comment
Я думаю, что должно быть более подробное объяснение того, почему это неправильно или так плохо. - person Charlie Parker; 22.10.2016
comment
@CharlieParker Есть. Это первая часть ответа Аарона Холла. - person Dinei; 24.02.2017
comment
Почему этот ответ нельзя пометить для удаления? Он уже получил 93 голоса против! - person codeforester; 20.10.2017
comment
@codeforester, возможно, потому, что отвечает на вопрос. - person Daniel F.; 13.04.2018
comment
Этот ответ все еще здесь, потому что многие опытные разработчики не согласны с использованием специализированных классов исключений. Хотел бы я отрицать комментарии. - person ctpenrose; 20.10.2018
comment
Это Python. Часто вы пишете сценарий из 20 строк для некоторой быстрой работы, которая может пойти не так, как надо, и создание общего исключения с информативным сообщением - лучший инструмент для этой работы. - person pooya13; 09.03.2021
comment
Когда я делаю только raise Exception("I know python!"), я не могу распечатать его след - person alper; 19.05.2021

В 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 Exception.

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
person N Randhawa    schedule 08.11.2016
comment
Обратите внимание, что PEP8 предпочитает exception(args) exception (args) - person Gloweye; 28.06.2019
comment
Также raise exception(args) from None означает, что текущее активное исключение было обработано и больше не представляет интереса. В противном случае, если вы вызываете исключение внутри блока except, и оно не обрабатывается, трассировки для обоих исключений будут показаны разделенными сообщением «Во время обработки указанного выше исключения произошло другое исключение». - person cg909; 04.04.2020

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

if 0 < distance <= RADIUS:
    #Do something.
elif RADIUS < distance:
    #Do something.
else:
    raise AssertionError("Unexpected value of 'distance'!", distance)
person Evgeni Sergeev    schedule 19.05.2015
comment
Это лучший случай для ValueError, чем для AssertionError, потому что нет проблем с утверждением (потому что здесь ничего не делается) - проблема в значении. Если в этом случае вам действительно нужен AssertionError, напишите assert distance > 0, 'Distance must be positive'. Но вы не должны проверять ошибки таким образом, потому что утверждения можно отключить (python -O). - person Two-Bit Alchemist; 17.09.2015
comment
@ Two-BitAlchemist Хорошее замечание. Идея упростилась, когда я написал простой пример выше. Во многих подобных случаях это условие, не связанное с конкретным значением. Скорее, смысл в том, что поток управления сюда никогда не попадет. - person Evgeni Sergeev; 17.09.2015
comment
@ Two-BitAlchemist Assertions можно отключить, да, но тогда вы вообще не должны использовать их для проверки ошибок? - person Evgeni Sergeev; 17.09.2015
comment
Смотря как. Я бы не позволил, чтобы это была моя единственная проверка ошибок в программе, которую я намеревался распространять. С другой стороны, я мог бы создать программу только для моих коллег и сказать им, что они используют ее на свой страх и риск, если они запускают ее с -O. - person Two-Bit Alchemist; 17.09.2015
comment
@ Two-BitAlchemist Для меня роль утверждений заключается не в проверке ошибок как таковой (для чего и предназначено тестирование), а в том, что они устанавливают ограждения в коде, через которые определенные ошибки не могут пройти. Так становится легче отслеживать и изолировать ошибки, которые неизбежно возникнут. Это просто хорошие привычки, которые требуют небольших усилий, а тестирование требует больших усилий и много времени. - person Evgeni Sergeev; 21.09.2015
comment
тестирование требует больших усилий и времени - правда, но не столько, сколько времени и усилий, которые оно экономит. - person Jonathan Hartley; 29.09.2015
comment
Ошибка может быть воспроизведена, если псевдоконстантный RADIUS также каким-то образом установлен отрицательным. - person Daerdemandt; 12.09.2016

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

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

Пример:

raise SystemExit

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

raise SystemExit("program exited")

это напечатает "программа завершена" в stderr перед закрытием программы.

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

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

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

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

person markemus    schedule 03.12.2018

Другой способ создания исключений - 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))
person Rehan Haider    schedule 27.02.2019
comment
не надежен, поскольку утверждения в CPython игнорируются, когда интерпретатор инициализируется с флагом оптимизации (-O); если вы хотите действительно контролировать выполнение программы, эй, это условие не должно произойти, но прервите его, если оно истинно, вручную raise AssertionError() - person cowbert; 24.09.2020

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

try:
    raise TypeError            #remove TypeError by any other error if you want
except TypeError:
    print('TypeError raised')
person Abhijeet.py    schedule 31.03.2020
comment
Можете ли вы объяснить, почему ваш пример хорош? Я прочитал все ответы на этот вопрос, и мне искренне любопытно. - person Brian; 21.10.2020

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

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

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

person LuisAFK    schedule 08.06.2021