python: Как узнать, какой тип исключения произошел?


230

У меня есть функция, вызываемая основной программой:

try:
    someFunction()
except:
    print "exception happened!"

но в середине выполнения функции возникает исключение, поэтому он переходит к exceptчасти.

Как я могу точно увидеть, что произошло в том, someFunction()что вызвало исключение?


9
Никогда не используйте голые except:(без голых raise), за исключением, может быть, один раз для каждой программы, и желательно не тогда.
Майк Грэм,

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

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

3
Внутри exceptблока исключение доступно через sys.exc_info()функцию - эта функция возвращает три значения, которые дают информацию об исключении, которое обрабатывается в данный момент.
Петр Доброгост

Ответы:


384

Все остальные ответы указывают на то, что вы не должны ловить общие исключения, но никто, кажется, не хочет говорить вам, почему, что важно для понимания, когда вы можете нарушить «правило». Вот объяснение. По сути, это так, что вы не скрываете

Поэтому, если вы позаботитесь о том, чтобы не делать ничего из этого, можно поймать общее исключение. Например, вы можете предоставить пользователю информацию об исключении другим способом, например:

  • Представлять исключения в виде диалогов в графическом интерфейсе
  • Перенос исключений из рабочего потока или процесса в управляющий поток или процесс в многопоточном или многопроцессорном приложении

Так как же поймать общее исключение? Есть несколько способов. Если вы просто хотите объект исключения, сделайте это так:

try:
    someFunction()
except Exception as ex:
    template = "An exception of type {0} occurred. Arguments:\n{1!r}"
    message = template.format(type(ex).__name__, ex.args)
    print message

Удостоверьтесь, что message пользователь обратил на себя внимание с трудом! Его печати, как показано выше, может быть недостаточно, если сообщение скрыто во многих других сообщениях. Неспособность привлечь внимание пользователей равносильно проглатыванию всех исключений, и если есть одно впечатление, которое вы должны были оставить после прочтения ответов на этой странице, это то, что это не очень хорошая вещь . Завершение блока исключений с помощью raiseоператора исправит проблему, прозрачно переосмыслив исключение, которое было перехвачено.

Разница между вышесказанным и использованием except:без каких-либо аргументов двояка:

  • Голый except:не дает вам объект исключения для проверки
  • Исключения SystemExit, KeyboardInterruptи GeneratorExitне пойманы выше код, который , как правило , то , что вы хотите. Смотрите иерархию исключений .

Если вам также нужна та же самая трассировка стека, которую вы получаете, если вы не перехватываете исключение, вы можете получить его вот так (по-прежнему внутри предложения исключений):

import traceback
print traceback.format_exc()

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

import logging
log = logging.getLogger()
log.exception("Message for you, sir!")

Если вы хотите копать глубже и исследовать стек, смотреть на переменные и т. Д., Используйте post_mortemфункцию pdbмодуля внутри блока exc:

import pdb
pdb.post_mortem()

Я обнаружил, что этот последний метод неоценим при поиске ошибок.


1
Я думаю, что traceback.print_exc () будет делать то же самое, что и ваш более сложный "".
Gurgeh

1
@Gurgeh Да, но я не знаю, хочет ли он напечатать это или сохранить это в файл или зарегистрировать это или сделать что-то еще с этим.
Лауриц В. Таулов

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

10
@Rik Думаю, тебе все это может понадобиться . Например, если у вас есть программа с графическим интерфейсом и бэкэндом, и вы хотите представить все исключения из бэкэнда в виде сообщений с графическим интерфейсом вместо того, чтобы ваша программа завершила трассировку стека. В таком случае вы должны перехватить универсальное исключение, создать текст обратной трассировки для диалогового окна, также зарегистрировать исключение и, если в режиме отладки, ввести посмертно.
Lauritz V. Thaulow

18
@RikPoggi: наивное мышление. Есть много разумных обстоятельств, когда вам нужно перехватывать исключения из чужого кода, и вы не знаете, какие исключения будут вызваны.
stackoverflowuser2010

63

Получите имя класса, к которому принадлежит объект исключения:

e.__class__.__name__

и использование функции print_exc () также напечатает трассировку стека, которая является важной информацией для любого сообщения об ошибке.

Как это:

from traceback import print_exc

class CustomException(Exception): pass

try:
    raise CustomException("hi")
except Exception, e:
    print 'type is:', e.__class__.__name__
    print_exc()
    # print "exception happened!"

Вы получите вывод, как это:

type is: CustomException
Traceback (most recent call last):
  File "exc.py", line 7, in <module>
    raise CustomException("hi")
CustomException: hi

И после печати и анализа код может решить не обрабатывать исключение и просто выполнить raise:

from traceback import print_exc

class CustomException(Exception): pass

def calculate():
    raise CustomException("hi")

try:
    calculate()
except Exception, e:
    if e.__class__ == CustomException:
        print 'special case of', e.__class__.__name__, 'not interfering'
        raise
    print "handling exception"

Вывод:

special case of CustomException not interfering

И переводчик печатает исключение:

Traceback (most recent call last):
  File "test.py", line 9, in <module>
    calculate()
  File "test.py", line 6, in calculate
    raise CustomException("hi")
__main__.CustomException: hi

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

from traceback import print_exc

class CustomException(Exception): pass

def calculate():
    raise CustomException("hi")

try:
    calculate()
except Exception, e:
    if e.__class__ == CustomException:
        print 'special case of', e.__class__.__name__, 'not interfering'
        #raise CustomException(e.message)
        raise e
    print "handling exception"

Вывод:

special case of CustomException not interfering
Traceback (most recent call last):
  File "test.py", line 13, in <module>
    raise CustomException(e.message)
__main__.CustomException: hi    

Обратите внимание, что traceback не включает calculate()функцию из строки, 9которая является источником исходного исключения e.


Если вы хотите сохранить отслеживающий в виде строки, вы можете использовать , traceback.format_exc()а также
Stevoisiak

1
e.__class__.__name__это так же type(e).__name__, как и предложенный ответ выше?
информационный

1
@information_interchange да. Содержание вопросов и принятых ответов со временем полностью изменилось. Стыдно, что другие участники не уведомлены SO техникой :(
Alex

14

Обычно вы не должны отлавливать все возможные исключения, try: ... exceptпоскольку они слишком широки. Просто поймайте те, которые ожидаются по любой причине. Если вам действительно нужно, например, если вы хотите узнать больше о какой-то проблеме во время отладки, вы должны сделать

try:
    ...
except Exception as ex:
    print ex # do whatever you want for debugging.
    raise    # re-raise exception.

17
Использование слова «никогда» здесь никогда не было так неправильно. Я использую try: ... except Exception:множество вещей, например, использование библиотек, зависящих от сети, или массажистку, которая может отправлять ей странные вещи. Естественно, у меня тоже есть правильная регистрация. Это важно, чтобы позволить программе продолжить работу в случае единственной ошибки во входных данных.
thnee

3
Вы когда-нибудь пытались перехватить все исключения, которые могут возникнуть при отправке электронной почты с помощью smtplib?
linusg

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

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

Хорошо, хорошо, вы правы, я перефразирую свой ответ, чтобы прояснить, что существуют правильные варианты использования для всеобщего охвата.
hochl

10

Если только somefunctionэто не очень плохо закодированная устаревшая функция, вам не нужно то, что вы просите.

Используйте несколько exceptпредложений, чтобы по-разному обрабатывать различные исключения:

try:
    someFunction()
except ValueError:
    # do something
except ZeroDivision:
    # do something else

Суть в том, что вы не должны ловить общие исключения, а только те, которые вам нужны. Я уверен, что вы не хотите скрывать неожиданные ошибки или ошибки.


8
Если вы используете стороннюю библиотеку, вы можете не знать, какие исключения будут возникать внутри нее. Как вы можете поймать их всех по отдельности?
stackoverflowuser2010

8

Большинство ответов указывают на except (…) as (…):синтаксис (правильно), но в то же время никто не хочет говорить о слоне в комнате, где слон - это sys.exc_info()функция. Из документации по SYS модуля (курсив мой):

Эта функция возвращает кортеж из трех значений, которые дают информацию об исключении, которое в настоящее время обрабатывается.
(…)
Если нигде в стеке не обрабатывается исключение, возвращается кортеж, содержащий три значения None. В противном случае возвращаются значения (тип, значение, обратная связь). Их значение: тип получает тип обрабатываемого исключения (подкласс BaseException); значение получает экземпляр исключения (экземпляр типа исключения); traceback получает объект traceback (см. Справочное руководство), который инкапсулирует стек вызовов в точке, где первоначально произошло исключение.

Я думаю, что это sys.exc_info()можно рассматривать как самый прямой ответ на первоначальный вопрос: как узнать, какой тип исключения произошел?


1
Это правильный ответ для меня, так как он решает проблему того, что происходит исключение, и что я должен поставить вместо голого except. Просто для полноты exctype, value = sys.exc_info()[:2]расскажет вам тип исключения, который затем может быть использован в except.
Ондрей Беркерт

5

try: someFunction () кроме Exception, exc:

#this is how you get the type
excType = exc.__class__.__name__

#here we are printing out information about the Exception
print 'exception type', excType
print 'exception msg', str(exc)

#It's easy to reraise an exception with more information added to it
msg = 'there was a problem with someFunction'
raise Exception(msg + 'because of %s: %s' % (excType, exc))

-1, поскольку использование exc.__class__.__name__уже было предложено в ответе Алекса - stackoverflow.com/a/9824060/95735
Петр Доброгост

3

Эти ответы хороши для отладки, но для программного тестирования исключения isinstance(e, SomeException)могут быть полезны, так как он также проверяет подклассы SomeException, так что вы можете создать функциональность, которая применяется к иерархиям исключений.


1

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

path = 'app.p'

def load():
    if os.path.exists(path):
        try:
            with open(path, 'rb') as file:
                data = file.read()
                inst = pickle.load(data)
        except Exception as e:
            inst = solve(e, 'load app data', easy=lambda: App(), path=path)()
    else:
        inst = App()
    inst.loadWidgets()

# e.g. A solver could search for app data if desc='load app data'
def solve(e, during, easy, **kwargs):
    class_name = e.__class__.__name__
    print(class_name + ': ' + str(e))
    print('\t during: ' + during)
    return easy

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

В показанном примере одним из решений может быть поиск данных приложения, хранящихся в другом месте, например, если файл 'app.p' был удален по ошибке.

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


0

Чтобы добавить к ответу Лорица, я создал декоратор / упаковщик для обработки исключений и журнал оболочек, какой тип исключения произошел.

class general_function_handler(object):
    def __init__(self, func):
        self.func = func
    def __get__(self, obj, type=None):
        return self.__class__(self.func.__get__(obj, type))
    def __call__(self, *args, **kwargs):
        try:
            retval = self.func(*args, **kwargs)
        except Exception, e :
            logging.warning('Exception in %s' % self.func)
            template = "An exception of type {0} occured. Arguments:\n{1!r}"
            message = template.format(type(e).__name__, e.args)
            logging.exception(message)
            sys.exit(1) # exit on all exceptions for now
        return retval

Это может быть вызвано для метода класса или отдельной функции с декоратором:

@general_function_handler

Смотрите мой блог о полном примере: http://ryaneirwin.wordpress.com/2014/05/31/python-decorators-and-exception-handling/


0

Вы можете начать, как рекомендовал Лауриц, с:

except Exception as ex:

а потом просто print exтак нравится

try:
    #your try code here
except Exception as ex:
    print ex

Можете ли вы уточнить немного, чтобы ваш ответ стоял один?
GHC

1
конечно: вы можете напечатать перехваченное исключение следующим образом: try: #your try code здесь, кроме Exception, как ex: print ex, теперь ошибка будет напечатана
Gura


-2

Ваш вопрос: «Как я могу точно увидеть, что произошло в someFunction (), которая вызвала исключение?»

Мне кажется, что вы спрашиваете не о том, как обрабатывать непредвиденные исключения в производственном коде (как предполагалось во многих ответах), а о том, как выяснить, что вызывает конкретное исключение во время разработки.

Самый простой способ - использовать отладчик, который может останавливаться там, где происходит неперехваченное исключение, предпочтительно не выходить, чтобы вы могли проверить переменные. Например, PyDev в интегрированной среде разработки Eclipse может сделать это. Чтобы включить это в Eclipse, откройте перспективу Debug, выберите Manage Python Exception Breakpointsв Runменю и проверьте Suspend on uncaught exceptions.


-4

Просто воздержитесь от перехвата исключения и трассировки, которую печатает Python, сообщит вам, что произошло исключение.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.