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

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

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

try и except: ловим ошибку и не падаем

Первый запасной план: try держит рискованный код, except — что делать, если он упал. Прыжки, пропуски и try внутри цикла — на живом коде.

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

Прошлый урок научил читать traceback: тип, место, сообщение. Но читать отчёт о падении приятно, когда падение — чужое. Настоящая сила в другом: сказать программе, что делать, если рискованная операция не удалась. Не падать, а переключиться на запасной план: подставить ноль, напечатать предупреждение, попросить данные ещё раз. Для этого в Python есть пара try и except — главный инструмент обработки исключений.

Запасной план: try и except

Схема проста. Слово try: открывает блок рискованного кода — строки с отступом, которые могут упасть. Слово except: — блок запасного плана: что делать, если в try случилось исключение. Вернёмся к разбору слова в число из первого урока, но теперь с планом Б:

первая ловля ошибки
try:
    n = int('двенадцать')
    print('Разобрали:', n)
except:
    print('Не получилось разобрать число')
print('Программа работает дальше')
Вывод
Не получилось разобрать число
Программа работает дальше

Никакого traceback: программа не упала. Python начал выполнять try, дошёл до int('двенадцать'), получил ValueError — и вместо печати traceback перескочил в except. Там выполнился запасной план, а после него программа продолжилась по обычному пути: последняя строка отработала как ни в чём не бывало. Блок try выполняет рискованный код, блок except держит запасной план, который срабатывает вместо падения.

Если ошибки не случилось

Подсунем в тот же код число, которое разбирается без проблем:

ошибки нет — except пропущен
try:
    n = int('42')
    print('Разобрали:', n)
except:
    print('Запасной план')
print('Дальше идёт обычный код')
Вывод
Разобрали: 42
Дальше идёт обычный код

Теперь в try всё прошло гладко, и блок except не выполнился ни разу: запасной план нужен только при аварии. Это важное свойство конструкции — except не запускается «на всякий случай», он строго реагирует на исключение. Поэтому в try пишут именно рискованный код, а не всю программу: лишний код в try — это лишние шансы случайно уронить его и зря запустить план Б.

Прыжок в except: остаток try пропускается

Исключение случается не в начале try, а в конкретной строке. Что происходит с остальными строками блока? Они пропускаются: прыжок в except происходит в момент сбоя, и выполнение в try не возвращается.

строка после аварии не выполняется
try:
    print('первая строка try')
    n = int('нет числа')
    print('эта строка не выполнится')
except:
    print('перешли в except')
print('после try')
Вывод
первая строка try
перешли в except
после try

Первая строка try успела напечататься, вторая упала — и четвёртая строка блока осталась невыполненной навсегда. Python не возвращается в try после except: попытка сделана, план Б отработан, программа идёт дальше. Если в рискованном коде есть строки, которые должны выполниться даже при ошибке, им место не внутри try.

Ловим деление на ноль

ZeroDivisionError из первого урока — классический кандидат на обработку: сумма на ноль участников делится в любой статистике.

запасной план для деления
a = 10
b = 0
try:
    print('Делим', a, 'на', b)
    result = a / b
    print('Результат:', result)
except:
    print('Делить на ноль нельзя — считаем по-другому')
print('Конец программы')
Вывод
Делим 10 на 0
Делить на ноль нельзя — считаем по-другому
Конец программы

Печать «Делим 10 на 0» успела сработать — она была до аварийной строки. Само деление упало, и Python ушёл в except, не выполнив печать результата. Заметьте: программа не просто не упала — она дошла до конца. В первом уроке тот же код печатал traceback и обрывался.

try внутри цикла: спасти весь список

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

одна плохая строка не уронит проход
values = ['3', '5', 'кошка', '7']
for v in values:
    try:
        n = int(v)
        print(v, '-> число', n)
    except:
        print(v, '-> не число')
Вывод
3 -> число 3
5 -> число 5
кошка -> не число
7 -> число 7

Слово «кошка» не разобралось — и вместо падения всего отчёта мы получили одну честную строку «кошка -> не число». Цикл продолжился и разобрал семёрку. Этот приём — сердце обработки ошибок в данных, и к нему раздел вернётся ещё не раз: в уроке про несколько except мы уточним типы, а в уроке про as e начнём печатать текст ошибки в лог.

try и except — не для всего

У конструкции есть жёсткая граница: она ловит ошибки времени выполнения. Ошибки синтаксиса Python обнаруживает до запуска, когда ни try, ни except ещё не существуют.

try не спасает от синтаксиса
try:
    print('привет'
except:
    print('не спасёт')
Файл целиком не запустится: Python найдёт незакрытую скобку при чтении кода и ответит SyntaxError: '(' was never closed. Ни try, ни except не выполнятся ни разу.

Вторая граница — минимальная комплектность. Одинокий try без запасного плана не имеет смысла, и Python проверяет это тоже — ещё до запуска:

try без except не запускается
try:
    x = 1
Python ответит SyntaxError: expected 'except' or 'finally' block. После try обязан идти хотя бы один блок except (или finally — о нём будет отдельный урок).

Шпаргалка по поведению try

СитуацияЧто делает программа
В try ошибка, except естьПрыжок в except, дальше по обычному пути
В try ошибки нетtry выполняется целиком, except пропускается
Строка после аварии внутри tryНе выполняется: прыжок происходит в момент сбоя
Ошибка вне tryОбычный traceback: программа падает
SyntaxError в кодеФайл не запускается: try тут бессилен

Остался последний кандидат — IndexError: обращение к списку по несуществующему номеру. Потренируемся ловить и его.

ловим обращение мимо списка
numbers = [10, 20, 30]
try:
    print(numbers[5])
except:
    print('Такого номера нет в списке')
print('Программа цела')
Вывод
Такого номера нет в списке
Программа цела
а без try — падение
numbers = [10, 20, 30]
print(numbers[5])
print('эта строка не выполнится')
Вывод
Traceback (most recent call last):
  File "app.py", line 2, in <module>
    print(numbers[5])
          ~~~~~~~^^^
IndexError: list index out of range
Тот же код без запасного плана: traceback, печать внизу не выполнилась, программа оборвалась.

Два блока с одними данными — два исхода. С try программа доходит до конца, без него обрывается на первой же строке с неверным индексом. Теперь у вас есть выбор для каждой рискованной операции: написать её как есть и получить traceback при сбое — или окружить try и except и решить, что делать при сбое. В следующем уроке мы сделаем обработчик точным: except ValueError ловит конкретный тип и не прячет чужие ошибки.

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

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

try:
    print('А')
    x = int('9')
    print('Б')
except:
    print('В')
print('Г')
try:
    print('А')
    x = int('девять')
    print('Б')
except:
    print('В')
print('Г')
for text in ['2', 'x']:
    try:
        print(int(text) * 10)
    except:
        print('мимо')
print('финиш')
Проверь себя
0 / 8

1. Что произойдёт с блоком except, если в try ошибок не было?

2. Внутри try упала вторая строка из пяти. Что будет с остальными строками try?

3. Обязательно ли после try писать except?

4. Спасёт ли try от SyntaxError — незакрытой скобки?

5. Зачем try кладут внутрь цикла for?

6. Какой код правильный по синтаксису?

7. Программа напечатала «Делим 10 на 0», затем строку except и «Конец программы». Упала ли она?

8. Почему длинный try на всю программу — плохая идея?

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

Дано value = 'семь'. Оберни разбор в try и except: при успехе напечатай «Получили число: N», при неудаче — «Не удалось разобрать: семь», а после try — строку «Готово». Программа не должна упасть.

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

Как работает try except в Python?

Python выполняет блок try строка за строкой. Если случилось исключение, выполнение перескакивает в except и после него программа продолжается. Если исключения не было, except пропускается целиком. Строки try после аварийной не выполняются.

Урок 3: except ValueError — конкретный тип

Можно ли писать except без указания типа ошибки?

Синтаксически можно: голый except ловит любые исключения. Но в настоящем коде так делать не рекомендуют — вместе с настоящей проблемой он прячет опечатки и баги. Лучше называть тип: except ValueError для разбора значений, except ZeroDivisionError для деления.

Что класть в блок try?

Только ту операцию, которой не доверяете: разбор строки в число, деление, обращение к словарю. Чем короче try, тем точнее видно, что именно упало. Весь остальной код пишется после except без отступа.

Выполнятся ли строки после аварийной внутри try?

Нет. Прыжок в except происходит в момент сбоя, и обратно в try программа не возвращается. Код, который должен выполниться при любом исходе, выносят за пределы try — или используют finally, о котором рассказано в уроке про else и finally.

Урок 6: else и finally

Может ли try быть внутри цикла?

Да, и это самая рабочая схема обработки данных: for перебирает строки, try оборачивает разбор каждой. Одна плохая строка уходит в except, остальные обрабатываются. Цикл продолжается после сбоя — весь список не падает из-за одного элемента.

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

«Блок try выполняет рискованный код, блок except держит запасной план, который срабатывает вместо падения.»

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

TelegramVK

Что дальше

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

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

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