Перейти к содержанию

412 уроков, 25 библиотек и челлендж «Что выведет код?» — бесплатно, код прямо в браузере

Начать обучение
Урок 5 из 20 Начальный 15 мин 100 XP

as e: объект ошибки в переменной

Обработчик узнаёт больше, если взять ошибку в переменную: except ValueError as e даёт тип, сообщение и аргументы исключения.

Редакция Питоники

До сих пор запасные планы были слепыми: except сообщал «не число», но не говорил, что именно Python пытался разобрать и почему отказался. А ведь в traceback эта информация была: invalid literal for int() with base 10: 'двенадцать'. Хорошая новость — её не обязательно терять: исключение можно поймать не только для реакции, но и для разбора. Для этого у except есть вторая форма — с переменной.

Берём объект ошибки: as e

Запись except ValueError as e: делает две вещи: ловит ValueError и кладёт объект ошибки в переменную e. Объект — это не строка, а полноценное значение со своими данными: типом, сообщением, аргументами.

три факта об ошибке
try:
    n = int('двенадцать')
except ValueError as e:
    print('Тип:', type(e).__name__)
    print('Сообщение:', e)
    print('Аргументы:', e.args)
Вывод
Тип: ValueError
Сообщение: invalid literal for int() with base 10: 'двенадцать'
Аргументы: ("invalid literal for int() with base 10: 'двенадцать'",)

Три способа заглянуть в пойманную ошибку. type(e).__name__ — имя типа в виде строки: ValueError. Печать самого e даёт сообщение — тот же текст, что завершал traceback в первом уроке. e.args — кортеж аргументов, с которыми ошибка была создана; у нашего ValueError он из одного элемента. Для первых двух задач достаточно печати e — но имя типа пригождается, когда один обработчик обслуживает несколько типов и нужно понять, кто именно прилетел.

Сообщение ошибки: у каждого типа своё

Сообщение — самое ценное в объекте: это человеческое описание причины. Печать e в запасном плане делает его информативным:

запасной план с подробностями
try:
    n = int('двенадцать')
except ValueError as e:
    print('Не разобрали:', e)
Вывод
Не разобрали: invalid literal for int() with base 10: 'двенадцать'

Теперь план Б не врёт обобщениями — он показывает, с каким значением разбор провалился. Сравните с «не разобрали» из прошлого урока: разница как между «заявка отклонена» и «заявка отклонена: не указана дата».

У разных типов сообщения разные по форме. KeyError вместо длинной фразы печатает сам недостающий ключ — в кавычках:

KeyError и его сообщение
d = {'Анна': 5}
try:
    print(d['Пётр'])
except KeyError as e:
    print('Тип:', type(e).__name__)
    print('Сообщение:', e)
Вывод
Тип: KeyError
Сообщение: 'Пётр'

Сообщение KeyError выглядит коротко: ключ в кавычках. Python печатает его так же, как в traceback первого урока, — это договорённость именно этого типа. Отсюда практический вывод: не полагайтесь на форму сообщения, полагайтесь на тип. Тип говорит, какая авария случилась; сообщение — детали для человека.

Лог в цикле: каждую аварию — в протокол

Соберём рабочую схему: суммарный разбор списка, где сбойные строки не роняют отчёт, а попадают в протокол с сообщением ошибки.

протокол сбоев
values = ['4', 'x', '9']
total = 0
for v in values:
    try:
        total += int(v)
    except ValueError as e:
        print('пропустил', repr(v), '-', e)
print('Итого:', total)
Вывод
пропустил 'x' - invalid literal for int() with base 10: 'x'
Итого: 13

Протокол говорит всё: какую строку пропустили (repr показывает её в кавычках, чтобы пустые строки были видны) и почему именно. Четвёрка и девятка вошли в сумму, икс — в протокол. Такая строка в логе экономит минуты расследования: видно и значение-виновник, и формулировку ошибки.

Имя переменной выбираете вы

e в записи as e — не зарезервированное слово, а обычное имя, которое придумывает автор кода. Курс по циклам любит короткие имена в заголовках циклов — здесь так же: e, err, ex. Смысл от имени не меняется:

та же переменная под именем err
try:
    n = int('abc')
except ValueError as err:
    print(type(err).__name__, '-', err)
Вывод
ValueError - invalid literal for int() with base 10: 'abc'

err напечатал то же, что печатал бы e: имя переменной — только ярлык для объекта ошибки. В одной программе стоит выбрать одно имя и держаться его, как выбирают единый стиль для переменных цикла.

e живёт только внутри except

Переменная из as e живёт только внутри except: на выходе из блока Python удаляет её сам. Проверим, что будет, если обратиться к e после блока:

обращение к e после блока
try:
    n = int('двенадцать')
except ValueError as e:
    print('поймали:', e)
print(e)
Вывод
поймали: invalid literal for int() with base 10: 'двенадцать'
Traceback (most recent call last):
  File "app.py", line 5, in <module>
    print(e)
          ^
NameError: name 'e' is not defined
Программа падает на печати после except: Python удалил переменную e при выходе из блока. Сообщение, если оно нужно дальше, стоит сохранить в отдельную переменную внутри except.

Первая печать сработала — внутри блока переменная была жива. Печать после блока упала с NameError: имя больше не существует. Python делает это специально, чтобы объекты ошибок не болтались в программе дольше необходимого. Правило простое: разбираем ошибку внутри except; что нужно на будущее — сохраняем в свою переменную, например message = str(e), ещё внутри блока.

опора на строковые методы падает
try:
    n = int('двенадцать')
except ValueError as e:
    print(e.upper())
Вывод
Traceback (most recent call last):
  File "app.py", line 2, in <module>
    n = int('двенадцать')
        ~~~^^^^^^^^^^^^^^
ValueError: invalid literal for int() with base 10: 'двенадцать'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "app.py", line 4, in <module>
    print(e.upper())
          ^^^^^^^
AttributeError: 'ValueError' object has no attribute 'upper'
Внутри except объект e ещё жив, но строковых методов у него нет: AttributeError. Средняя часть traceback («During handling...») сообщает, что новая ошибка случилась прямо во время обработки первой, — о цепочках ошибок в разделе будет отдельный урок. Верный путь — str(e).upper().

Шпаргалка по объекту ошибки

ВыражениеЧто даётПример значения
print(e) или str(e)Сообщение ошибкиinvalid literal for int() with base 10: 'x'
type(e).__name__Имя типа строкойValueError
e.argsКортеж аргументов("invalid literal ...",)
message = str(e)Сохранение сообщения на будущееработает только внутри except
e.upper()Ошибка: объект не строкаAttributeError

Мы научились не только ловить ошибку, но и забирать её данные: тип для ветвления, сообщение для протокола. Дальше — два хвоста try, которые расширяют конструкцию: else выполнится при успехе, а finally — при любом исходе. А цепочка обработчиков из четвёртого урока теперь может писать разные сообщения для разных типов.

Что выведет код?

Сначала предскажи ответ в голове — это главный навык программиста.

try:
    n = int('42')
except ValueError as e:
    print('ошибка:', e)
print('готово')
try:
    n = int('семь')
except ValueError as e:
    print(type(e).__name__)
    print(e)
words = ['3', 'нет']
total = 0
for w in words:
    try:
        total += int(w)
    except ValueError as e:
        print('мимо:', e)
print(total)
Проверь себя
0 / 8

1. Что делает запись except ValueError as e?

2. Как получить имя типа ошибки строкой?

3. Что напечатает print(e) для пойманного KeyError('Пётр')?

4. Что случится с переменной e после выхода из блока except?

5. Что вернёт e.args у ValueError('сообщение')?

6. Почему e.upper() падает с AttributeError?

7. Можно ли вместо e использовать другое имя — err?

8. Зачем печатать сообщение ошибки в запасном плане?

Карточки терминов
Запомнено: 0 / 7
Практика

Дан список values = ['3', 'кот']. Посчитай сумму разобранных чисел, а сбойные строки пропускай с протоколом: «отклонено: <сообщение ошибки>». В конце напечатай сумму.

practice.py
Вопросы и ответы по уроку

Что значит as e в except python?

Это сохранение пойманного объекта ошибки в переменную: except ValueError as e кладёт исключение в e. Внутри блока доступны тип (type(e).__name__), сообщение (print(e)) и аргументы (e.args). Имя e — произвольное: подойдёт err или ex.

Урок 6: else и finally

Как напечатать текст ошибки python?

Взять объект ошибки через as и напечатать его: except ValueError as e: print(e). print сам приводит объект к строке и печатает сообщение — тот же текст, что в последней строке traceback.

Сколько живёт переменная e из except?

Только внутри блока except: на выходе Python удаляет её, и обращение даёт NameError: name 'e' is not defined. Если сообщение нужно дальше по программе, сохраните его внутри блока: message = str(e).

Можно ли вызвать строковые методы у объекта ошибки?

Нет, напрямую нельзя: e.upper() упадёт с AttributeError, потому что объект исключения — не строка. Сначала приведите: str(e).upper(). Печать print(e) работает без приведения.

Чем полезно e.args?

Это кортеж аргументов, с которыми ошибка была создана: у ValueError('сообщение') args равен ('сообщение',). Пригодится, когда сообщение нужно как отдельное значение без строкового представления всего объекта.

Понравился урок? Сошлитесь на него

«Переменная из as e живёт только внутри except: на выходе из блока Python удаляет её сам.»

Скопируйте готовую ссылку в формате HTML, Markdown или чистый адрес и вставьте в статью на Habr, VC, Telegram-канал или свой блог — так о проекте узнают новые читатели.

TelegramVK

Что дальше

Осталось в разделе: 20 из 20. Прогресс хранится в вашем браузере.

Похожие уроки по темам

Подобраны автоматически по пересечению тем и ключевых слов.