python: Как узнать, какой тип исключения произошел?
но в середине выполнения функции он вызывает исключение, поэтому он переходит к части except . Как я могу точно увидеть, что произошло в someFunction() , вызвавшем исключение?
Никогда не используйте голые, except: (без голого raise ), за исключением, может быть, одного раза на программу, и желательно не тогда.
Если вы используете несколько предложений, except вам не нужно проверять тип исключения, что обычно делается для того, чтобы действовать в соответствии с конкретным типом исключения.
Если вам небезразличен тип исключения, это потому, что вы уже рассмотрели, какие типы исключений могут возникать логически.
Внутри except блока исключение доступно через sys.exc_info() — эта функция возвращает кортеж из трех значений, которые дают информацию об исключении, которое обрабатывается в настоящее время.
12 ответов
Другие ответы на все указывают на то, что вы не должны улавливать общие исключения, но никто, кажется, не хочет говорить вам, почему это важно для понимания, когда вы можете нарушить «правило». Здесь объяснение. В принципе, это так, что вы не скрываете:
Итак, до тех пор, пока вы берете на себя заботу о том, чтобы ничего не делать, все в порядке, чтобы поймать общее исключение. Например, вы могли бы предоставить информацию об исключении для пользователя другим способом, например:
- Существующие исключения как диалоги в графическом интерфейсе
- Передавать исключения из рабочего потока или процесса в управляющий поток или процесс в многопоточном или многопроцессорном приложении.
Итак, как поймать общее исключение? Есть несколько способов. Если вы просто хотите объект исключения, сделайте следующее:
try: someFunction() except Exception as ex: template = "An exception of type occurred. Arguments:\n" message = template.format(type(ex).__name__, ex.args) print message
Удостоверьтесь, что message доведено до сведения пользователя очень трудно! Печатать его, как показано выше, может быть недостаточно, если сообщение захоронено множеством других сообщений. Неспособность привлечь внимание пользователей равносильна проглатыванию всех исключений, и если у вас будет какое-то впечатление, что вам следует уйти после прочтения ответов на этой странице, это не очень хорошо. Завершение блока except с помощью оператора raise устраняет проблему путем прозрачного повторения исключения, которое было обнаружено.
Разница между приведенным выше и использованием только except: без каких-либо аргументов двояка:
- Голый except: не дает вам объект исключения для проверки
- Исключения SystemExit , KeyboardInterrupt и GeneratorExit не пойманы указанным выше кодом, что обычно является тем, что вы хотите. См. Иерархию исключений .
Если вы также хотите получить ту же стек, что и вы, если не поймаете исключение, вы можете получить это так (все еще внутри предложения 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()
Я нашел этот последний метод бесценным при поиске ошибок.
@Gurgeh Да, но я не знаю, хочет ли он напечатать это или сохранить это в файл или зарегистрировать это или сделать что-то еще с этим.
Я не отказывался от голосования, но я бы сказал, что это потому, что вы должны были вначале сказать, что вам не нужно ничего из этого, но вот как это можно сделать . А может быть потому, что вы предлагаете поймать общее исключение.
@Rik Думаю, тебе все это может понадобиться. Например, если у вас есть программа с графическим интерфейсом и бэкэндом, и вы хотите представить все исключения из бэкэнда в виде сообщений с графическим интерфейсом вместо того, чтобы ваша программа завершила трассировку стека. В таком случае вы должны перехватить универсальное исключение, создать текст обратной трассировки для диалогового окна, также зарегистрировать исключение и, если в режиме отладки, ввести после смерти.
@RikPoggi: наивное мышление. Существует много разумных обстоятельств, когда вам нужно перехватывать исключения из чужого кода, и вы не знаете, какие исключения будут вызваны.
Этот посмертный трюк помог мне сузить тип исключения objc.error который является своего рода objc.error .
Получите имя класса, к которому принадлежит объект исключения:
и использование функции 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 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 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 raise CustomException(e.message) __main__.CustomException: hi
Обратите внимание, что traceback не включает функцию calc calculate() из строки 9 которая является источником исходного исключения e .
Если вы хотите сохранить трассировку в виде строки, вы также можете использовать traceback.format_exc()
Обычно вы не должны улавливать все возможные исключения с помощью try: . except , поскольку это слишком широко. Просто поймайте те, которые, как ожидается, произойдут по любой причине. Если вам действительно нужно, например, если вы хотите узнать больше о какой-либо проблеме во время отладки, вы должны сделать
try: . except Exception as ex: print ex # do whatever you want for debugging. raise # re-raise exception.
Использование слова «никогда» здесь никогда не было так неправильно. Я использую try: . except Exception: вокруг множества вещей, например, использование библиотек, зависящих от сети, или массажистка, которая может отправлять ей странные вещи. Естественно, у меня тоже есть правильная регистрация. Это важно, чтобы позволить программе продолжить работу в случае единственной ошибки во входных данных.
Вы когда-нибудь пытались перехватить все исключения, которые могут возникнуть при отправке электронной почты с помощью smtplib ?
Могут быть некоторые особые случаи, когда необходимо перехватывать все исключения, но на общем уровне вы должны просто поймать то, что ожидаете, чтобы случайно не скрыть ошибки, которые вы не ожидали. Хорошая регистрация тоже хорошая идея, конечно.
Вполне разумно ловить все исключения. Если вы вызываете стороннюю библиотеку, вы можете не знать, какие исключения будут вызываться в этой библиотеке. В таком случае единственным выходом является перехват всех исключений, например, чтобы записать их в файл.
Хорошо, хорошо, вы правы, я перефразирую свой ответ, чтобы прояснить, что существуют правильные варианты использования для всех.
Если somefunction — очень плохо кодированная устаревшая функция, вам не нужно то, что вы просите.
Используйте несколько except для обработки различными способами различных исключений:
try: someFunction() except ValueError: # do something except ZeroDivision: # do something else
Главное, что вы не должны ловить общее исключение, а только те, которые вам нужны. Я уверен, что вы не хотите замаскировать неожиданные ошибки или ошибки.
Если вы используете стороннюю библиотеку, вы можете не знать, какие исключения будут возникать внутри нее. Как вы можете поймать их всех по отдельности?
Большинство ответов указывают на except (…) as (…): синтаксис (правильно), но в то же время никто не хочет говорить о слоне в комнате, где слоном является sys.exc_info() . Из документации модуля sys (выделение мое):
Эта функция возвращает кортеж из трех значений, которые дают информацию об исключении, которое в данный момент обрабатывается.
(. )
Если нигде в стеке не обрабатывается исключение, возвращается кортеж, содержащий три значения None. В противном случае возвращаются значения (тип, значение, обратная связь). Их значение таково: тип получает тип обрабатываемого исключения (подкласс BaseException); значение получает экземпляр исключения (экземпляр типа исключения); traceback получает объект traceback (см. Справочное руководство), который инкапсулирует стек вызовов в точке, где первоначально произошло исключение.
Я думаю, что sys.exc_info() можно рассматривать как самый прямой ответ на первоначальный вопрос: как узнать, какой тип исключения произошел?
Это правильный ответ для меня, так как он решает проблему того, что происходит исключение, и что я должен поставить вместо голого, except . Просто для полноты exctype, value = sys.exc_info()[:2] сообщит вам тип исключения, который затем можно будет использовать для except .
попробовать: 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 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
Вот как я обрабатываю свои исключения. Идея состоит в том, чтобы попытаться решить проблему, если это легко, а затем добавить более желательное решение, если это возможно. Не решайте проблему в коде, который генерирует исключение, или этот код теряет следы исходного алгоритма, который должен быть записан в точку. Однако передайте данные, необходимые для решения проблемы, и верните лямбду на всякий случай, если вы не сможете решить проблему за пределами кода, который ее генерирует.
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 был удален по ошибке.
На данный момент, поскольку запись обработчика исключений не является умной идеей (мы пока не знаем, как лучше ее решить, потому что дизайн приложения будет развиваться), мы просто возвращаем простое исправление, которое должно действовать как мы «запуск приложения в первый раз (в этом случае).