python: Как узнать, какой тип исключения произошел? - PullRequest
182 голосов
/ 22 марта 2012

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

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

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

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

Ответы [ 12 ]

307 голосов
/ 22 марта 2012

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

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

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

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

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 внутри блока исключений:

import pdb
pdb.post_mortem()

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

38 голосов
/ 22 марта 2012

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

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.

13 голосов
/ 22 марта 2012

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

try:
    ...
except Exception as ex:
    print ex # do whatever you want for debugging.
    raise    # re-raise exception.
8 голосов
/ 22 марта 2012

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

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

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

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

7 голосов
/ 20 декабря 2017

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

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

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

5 голосов
/ 22 марта 2012

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 голос
/ 17 декабря 2016

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

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 голосов
/ 24 марта 2015

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

except Exception as ex:

, а затем просто print ex вот так:

try:
    #your try code here
except Exception as ex:
    print ex
0 голосов
/ 29 июля 2014

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

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

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

0 голосов
/ 01 июня 2014

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

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/

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...