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

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

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

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

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

12 ответов


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

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

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

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

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 доводится до сведения пользователя труднодоступным способом! Печать его, как показано выше, может быть недостаточно, если сообщение похоронено во многих других сообщениях. Неспособность привлечь внимание пользователей равносильна проглатыванию всех исключений, и если есть одно впечатление, которое вы должны были уйти после прочтения ответов на этой странице, это то, что это не хороший вещь. Завершение блока except с помощью raise statement исправит проблему, прозрачно перезапустив исключение, которое было поймано.

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

  • голой except: не дает вам объект исключения для проверки
  • исключения SystemExit, KeyboardInterrupt и GeneratorExit не пойманы вышеуказанным кодом, который обычно является тем, что вы хотите. Увидеть иерархия исключений.

если вы также Хотите тот же stacktrace, который вы получаете, Если вы не поймаете исключение, вы можете получить это так (все еще внутри предложения except):

import traceback
print traceback.format_exc()

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

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

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

import pdb
pdb.post_mortem()

я нашел этот последний метод неоценим при охоте на жучков.


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

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 не включает


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

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

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

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

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

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


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


попробовать: someFunction() кроме исключения, искл:

#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))

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

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.

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

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


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

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/


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

except Exception as ex:

и просто print ex вот так:

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

фактическое исключение может быть захвачено следующим образом:

try:
    i = 1/0
except Exception as e:
    print e

вы можете узнать больше об исключениях из Учебник Python.


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

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

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


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