Як правильно отримати повідомлення про виключення в Python


97

Який найкращий спосіб отримувати повідомлення про винятки із компонентів стандартної бібліотеки в Python?

Я помітив, що в деяких випадках ви можете отримати його через таке messageполе:

try:
  pass
except Exception as ex:
  print(ex.message)

але в деяких випадках (наприклад, у разі помилок сокета) вам потрібно зробити щось подібне:

try:
  pass
except socket.error as ex:
  print(ex)

Цікаво, чи існує якийсь стандартний спосіб охопити більшість із цих ситуацій?


1
Ви поєднуєте дві різні речі - except Foo as bar:це те саме, що except Foo, bar:(за винятком того, що перше нове, і буде продовжувати працювати в 3.x), незалежно від того, чи помилка має messageатрибут, чи ні.
jonrsharpe

1
@FrozenHeart Ви запитуєте, доступ .messageдо помилки є стандартним способом чи ні?
Anand S Kumar

ваш другий приклад print(msg)- це просто ярлик дляprint(str(msg))
Сало

@Anand S Кумар Так. Чи вдасться це в будь-якому випадку?
FrozenHeart

4
Я вважаю, що доступ messageзастарілий.
Джонатан Рейнхарт,

Відповіді:


94

Якщо ви переглянете документацію щодо вбудованих помилок , то побачите, що більшість Exceptionкласів призначають перший аргумент як messageатрибут. Не всі з них роблять це.

Примітно, що EnvironmentError(з підкласами IOErrorта OSError) має перший аргумент errno, другий з strerror. Немає message... strerrorприблизно аналогічного тому, що зазвичай було б message.

Більш загально, підкласи Exceptionможуть робити все, що хочуть. Вони можуть мати або не мати messageатрибут. Майбутні вбудовані Exceptionмодулі можуть не мати messageатрибута. Будь-який Exceptionпідклас, імпортований із сторонніх бібліотек або коду користувача, може не мати messageатрибута.

Я думаю, що правильний спосіб вирішити це - визначити конкретні Exceptionпідкласи, які ви хочете зловити, а потім зловити лише ті, замість усього, за допомогою except Exception, а потім використовувати будь-які атрибути, які конкретний підклас визначає, як ви хочете.

Якщо вам printщось потрібно , я думаю, що друк самого зловленого Exception, швидше за все, зробить те, що ви хочете, незалежно від того, має він messageатрибут чи ні.

Ви також можете перевірити наявність атрибута повідомлення, якщо хочете, наприклад, але я б насправді не пропонував це, оскільки це просто здається безладним:

try:
    pass
except Exception as e:
    # Just print(e) is cleaner and more likely what you want,
    # but if you insist on printing message specifically whenever possible...
    if hasattr(e, 'message'):
        print(e.message)
    else:
        print(e)

Дякую за відповідь. Чи є якась особлива причина використовувати str(ex)замість просто ex?
FrozenHeart

3
@FrozenHeart - print()автоматично телефонує str()вам. Немає причин викликати його вручну самостійно, хоча це нешкідливо, оскільки виклик str()рядка просто поверне сам рядок.
ArtOfWarfare

1
@ArtOfWarfare Я думаю, що можуть бути проблеми, str()якщо повідомлення має тип unicode.
кратенко

35

Щоб покращити відповідь, надану @artofwarfare , ось що я вважаю більш акуратним способом перевірити messageатрибут та роздрукувати його або надрукувати Exceptionоб’єкт як запасний варіант.

try:
    pass 
except Exception as e:
    print getattr(e, 'message', repr(e))

Виклик reprнеобов’язковий, але я вважаю це необхідним у деяких випадках використання.


Оновлення №1:

Після коментаря @MadPhysicist , ось доказ того, чому заклик reprможе бути необхідним. Спробуйте запустити такий код у своєму інтерпретаторі:

try:
    raise Exception 
except Exception as e:
    print(getattr(e, 'message', repr(e)))
    print(getattr(e, 'message', str(e)))

Оновлення №2:

Ось демонстраційний файл зі специфікою для Python 2.7 та 3.5: https://gist.github.com/takwas/3b7a6edddef783f2abddffda1439f533


1
getattrвидає виняток, якщо переданий об'єкт не має запитуваного атрибута. Якщо ви хочете зробити що - щось подібне, я думаю , ви повинні використовувати необов'язковий третій аргумент в користь getattr, наприклад: print getattr(e, 'message', e). Сперечно краще, ніж те, що я зробив. Іншим варіантом буде print(e.message if hasattr(e, 'message') else e).
ArtOfWarfare

2
Ви майже напевно хотіли б strзамість цього repr.
Божевільний фізик

2
Порівняно з str, reprпросто додає назву класу. Замість використання reprя пропоную використовувати print('{e.__class__.__name__}: {e}'.format(e=e)). Друкована продукція чистіша і дотримується результату самого підвищення.
kadee

1
Виходячи з вищевикладеного, я знайшов найбільш корисним для мене , щоб бути'{e.__class__.__module__}.{e.__class__.__name__}: {e}'
Шаді

1
Дякую!! repr(e)наразі було єдиним рішенням, яке допомогло мені надрукувати принаймні мінімальну кількість інформації про помилку, яку я виявляю за певних обставин. repr(e)приносить KeyError(0,)(саме в цьому полягає помилка), тоді як str(e)або e.messageдає лише 0або взагалі нічого відповідно.
FlorianH

0

У мене була та сама проблема. Я думаю, що найкращим рішенням є використання log.exception, який автоматично роздрукує трасування стека та повідомлення про помилку, наприклад:

try:
    pass
    log.info('Success')
except:
    log.exception('Failed')

1
Що це таке log? Я щойно спробував import logPython 2.7.13 і отримав повідомлення про відсутність модуля з таким ім’ям. Це те, що ви додали за допомогою, pipчи це було додано в новішій версії python (я знаю, я знаю, мені потрібно оновити до Python 3 ... Я зроблю це, як тільки CentOS припинить доставку з Python 2 за замовчуванням ...)
ArtOfWarfare

6
Він / вона, ймовірно, використовує модуль ведення журналу з python, а потім створює екземпляр реєстратора як змінну журналу. import logging\ nlog = logging.getLogger(__name__)
Хосепас

0

У мене теж була така сама проблема. Заглибившись у це, я виявив, що клас Exception має argsатрибут, який фіксує аргументи, які були використані для створення винятку. Якщо звузити винятки, які, крім того, потраплять до підмножини, ви зможете визначити, як вони були побудовані, і, отже, який аргумент містить повідомлення.

try:
   # do something that may raise an AuthException
except AuthException as ex:
   if ex.args[0] == "Authentication Timeout.":
      # handle timeout
   else:
      # generic handling
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.