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модуля всередині блоку, крім:

import pdb
pdb.post_mortem()

Я знайшов цей останній метод неоціненним при полюванні на помилок.


1
traceback.print_exc () зробив би те саме, що і ваш складніший "" .jedin-thing, я думаю.
Гурге

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

Я не прихилявся, але скажу, що це тому, що ви повинні були поставити величезну жирову занепокоєння, сказавши, що вам нічого цього не потрібно, але ось як це можна зробити . А може тому, що ви пропонуєте спіймати загальний виняток.
Рік Поггі

10
@Rik Я думаю, що вам може знадобитися все це. Наприклад, якщо у вас є програма з графічним графічним інтерфейсом і заднім числом, і ви хочете представити всі винятки з бекенда у вигляді повідомлень графічного інтерфейсу, замість того, щоб програма виходила із сліду стека. У такому випадку вам слід спіймати загальний виняток, створити текст відстеження для діалогового вікна, також увімкнути виняток і, якщо в режимі налагодження, ввести посмертний вигляд.
Лауріц В. Таулов

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__, як підказує відповідь вище?
information_interchange

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

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 отримує об'єкт відстеження (див. Довідковий посібник), який інкапсулює стек виклику в точці, де виняток відбувся спочатку.

Я думаю, що це sys.exc_info()можна вважати найбільш прямою відповіддю на початкове запитання " Як мені знати, який тип винятку стався?"


1
Це правильна відповідь для мене, оскільки вона вирішує проблему того, який виняток відбувається, і що я повинен поставити замість голого except. Тільки задля повноти, exctype, value = sys.exc_info()[:2]розповість вам тип винятку, який потім можна використовувати на except.
Ондрей Беркерт

5

спробуйте: someFunction () крім винятку, 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
Piotr Dobrogost

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
впевнений: ви можете надрукувати спійманий виняток так: спробуйте: # ваш код спроби тут, за винятком винятку, як ex: print ex now, помилка буде надрукована
Gura


-2

Ваше запитання: "Як я можу точно побачити, що трапилося в деякихFunction (), які спричинили виняток?"

Мені здається, ви не питаєте про те, як поводитися з непередбаченими винятками у виробничому коді (як передбачається багато відповідей), а як з’ясувати, що спричиняє конкретний виняток під час розробки.

Найпростіший спосіб - це використовувати налагоджувач, який може зупинятися там, де відбувається вимкнено виняток, бажано, щоб він не вийшов, щоб ви могли перевірити змінні. Наприклад, PyDev в IDE з відкритим кодом Eclipse може це зробити. Щоб увімкнути це в Eclipse, відкрийте перспективу налагодження, виберіть Manage Python Exception Breakpointsу Runменю та поставте прапорець Suspend on uncaught exceptions.


-4

Просто утримайтеся від вибору винятку і прослідкування, яке друкує Python, скаже вам, який виняток стався.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.