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

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

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

Что такое исключение: первая ошибка в программе

Первая ошибка в программе: читаем traceback за десять секунд — тип ошибки, место и сообщение. Разбираем четыре типовых падения на живом коде.

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

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

Программа выполняется строка за строкой

Python читает программу сверху вниз и выполняет строки по одной. Пока каждая строка срабатывает, выполнение доходит до конца файла:

программа без происшествий
prices = [100, 250, 80]
print('Товаров:', len(prices))
print('Сумма:', prices[0] + prices[1] + prices[2])
Вывод
Товаров: 3
Сумма: 430

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

Первая авария: разбор строки в число

Возьмём частую операцию — превращение текста в число. Функция int принимает строку с цифрами и возвращает целое число. Пробелы по краям она терпит, минус понимает:

int умеет числовой текст
print(int('18'))
print(int('0'))
print(int('  7  '))
print(int('-7'))
Вывод
18
0
7
-7

А теперь подсунем int слово вместо числа. Слово «двенадцать» — это текст, но не числовой текст: цифр в нём нет. Операция невозможна, и Python реагирует отчётом:

первый traceback
n = int('двенадцать')
print('Разобрали:', n)
Вывод
Traceback (most recent call last):
  File "app.py", line 1, in <module>
    n = int('двенадцать')
        ~~~^^^^^^^^^^^^^^
ValueError: invalid literal for int() with base 10: 'двенадцать'
Код не запускается до конца: программа падает на первой строке. Путь к файлу в traceback будет вашим (показан условный app.py), а вид кареток чуть отличается между версиями Python.

Разбираем отчёт построчно, снизу вверх — именно так его и читают. Последняя строка traceback — тип ошибки и её сообщение: читать traceback начинают именно с неё. Тип здесь — ValueError, «ошибка значения»: сама операция существует, но значение неподходящее. Сообщение поясняет: invalid literal for int() with base 10 — «текст нельзя разобрать как целое число». Строки выше показывают место: файл app.py, строка 1, и саму строку кода с каретками под виновником. Заголовок Traceback (most recent call last) сообщает, что ниже — цепочка вызовов, где последнее место — то, где всё сломалось. Пока у нас один уровень, но в больших программах по нему виден весь путь до аварии.

вторая авария: деление на ноль
total = 100
participants = 0
print('Делим сумму на участников')
print(total / participants)
Вывод
Делим сумму на участников
Traceback (most recent call last):
  File "app.py", line 4, in <module>
    print(total / participants)
          ~~~~~~^~~~~~~~~~~~~~
ZeroDivisionError: division by zero
Программа падает на делении: две строки до аварии успели выполниться и напечататься, строка с делением — нет.

Обратите внимание: две строки до аварии напечатались, а авария случилась на четвёртой. Traceback всегда указывает точную строку, на которой выполнение остановилось, — всё до неё уже сработало, всё после неё не начнётся. Тип ZeroDivisionError говорит сам за себя: делить на ноль нельзя, и Python отказывается притворяться, что можно.

третья авария: строка плюс число
price = '3'
count = 3
total = price + count
print('Итого:', total)
Вывод
Traceback (most recent call last):
  File "app.py", line 3, in <module>
    total = price + count
            ~~~~~~^~~~~~~
TypeError: can only concatenate str (not "int") to str
Программа падает на сложении строки с числом: TypeError — операция не применима к таким типам данных.

Тут причина другая. Сложение существует и для строк, и для чисел, но Python не складывает строку с числом: типы не совпали. Отсюда и имя — TypeError, ошибка типа. Сообщение расшифровывается так: «строку можно соединить только со строкой, а не с int». Кстати, проверять типы значений можно функцией type — она печатает класс, к которому относится значение:

какие бывают типы данных
print(type(18))
print(type('18'))
print(type(3.5))
Вывод
<class 'int'>
<class 'str'>
<class 'float'>

Таблица типовых падений

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

ТипКогда случаетсяПример
ValueErrorОперация верная, значение неподходящееint('двенадцать')
TypeErrorОперация не применима к таким типам'3' + 3
ZeroDivisionErrorДеление на ноль100 / 0
NameErrorИмя не найдено: опечатка или забыли создатьprint(totl)
KeyErrorКлюча нет в словареguests['Вера']
IndexErrorИндекса нет в спискеitems[10]
SyntaxErrorКод написан не по правилам языкаprint('привет'

Место ошибки: файл, строка, цепочка

Средние строки traceback отвечают на вопрос «где». Запись File "app.py", line 2, in <module> читается так: файл app.py, строка 2, на верхнем уровне программы (не внутри функции — тогда вместо <module> стояло бы её имя). Под ней печатается сама строка кода с каретками под точным местом аварии. Посмотрим на NameError — типичную опечатку в имени:

четвёртая авария: опечатка в имени
orders = 12
print('Заказов:', totl)
Вывод
Traceback (most recent call last):
  File "app.py", line 2, in <module>
    print('Заказов:', totl)
                      ^^^^
NameError: name 'totl' is not defined
Программа падает: имени totl не существует. Каретки показывают ровно то слово, которое Python не смог найти.

Сообщения NameError звучат обидно понятно: name 'totl' is not defined — «имя totl не определено». В девяти случаях из десяти это опечатка: переменная orders объявлена, а печатаем мы несуществующий totl. Traceback подсвечивает каретками само имя — чинится за секунду.

пятая авария: ключа нет в словаре
guests = {'Анна': 2, 'Борис': 1}
print('Вера приходила:', guests['Вера'])
print('Конец программы')
Вывод
Traceback (most recent call last):
  File "app.py", line 2, in <module>
    print('Вера приходила:', guests['Вера'])
                             ~~~~~~^^^^^^^^
KeyError: 'Вера'
Программа падает на обращении к словарю: ключа Вера в нём нет. Третья строка не выполнится никогда — выполнение остановилось выше.

Ключ в сообщении печатается в кавычках — Python показывает, какого именно ключа не хватило. А строка «Конец программы» так и не напечаталась: это лучшая демонстрация того, что исключение останавливает программу. Всё, что написано ниже аварийной строки, мертво, пока ошибка не обработана. Обработка — тема следующего урока, а пока закрепим главный навык этого.

успешный вариант тех же данных
guests = {'Анна': 2, 'Борис': 1, 'Вера': 3}
print('Вера приходила:', guests['Вера'])
print('Конец программы')
Вывод
Вера приходила: 3
Конец программы

Проверка заранее: ключ есть или нет

У KeyError есть и мирный путь — проверить ключ заранее оператором in. Это не обработка ошибки, а профилактика, и для одного словаря она читается легко:

проверка ключа оператором in
guests = {'Анна': 2, 'Борис': 1}
name = 'Вера'
if name in guests:
    print(name, 'приходила:', guests[name])
else:
    print(name, 'не приходила')
Вывод
Вера не приходила

Для одного-двух ключей профилактика хороша. Когда данных много, проверка каждой строки раздувает код — и тогда на сцену выходит запасной план: try и except из следующего урока.

Алгоритм чтения traceback

Соберём навык урока в три шага. Он работает для любой аварии — от первой опечатки до чужой библиотеки.

  • прочитать последнюю строку: тип ошибки и сообщение — что сломалось;
  • найти своё имя файла в цепочке вызовов и номер строки — где сломалось;
  • посмотреть на строку кода с каретками — на чём именно сломалось.

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

Исключение — это сообщение с именем типа, местом и текстом, и теперь вы умеете его читать. Дальше — научиться на такие сообщения отвечать: не падать, а переключаться на запасной план.

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

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

print('Начало')
numbers = [10, 20, 30]
print(numbers[1])
print(numbers[5])
print(int(' 15 '))
print(int('15 кг'))
x = 5
y = 0
print('Ответ:', x / y)
print('Конец')
Проверь себя
0 / 8

1. С какой строки traceback принято начинать чтение?

2. Что означает тип ошибки ValueError в traceback?

3. Программа упала на строке 4. Выполнятся ли строки 5 и дальше?

4. Что означает запись File "app.py", line 2, in <module> в traceback?

5. Какая ошибка возникнет у print(guests['Вера']), если ключа Вера в словаре нет?

6. total = '3' + 3 упал с TypeError. Что означает этот тип?

7. Чем SyntaxError отличается от ValueError и TypeError?

8. Программа напечатала «Делим сумму на участников» и упала на следующей строке. Что это говорит о порядке выполнения?

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

Дана строка n_str = '50'. Преврати её в число, умножь на 2 и напечатай в формате «50 * 2 = 100» — одним print с f-строкой.

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

Что такое исключение в Python простыми словами?

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

Урок 2: try и except — ловим ошибку

Как быстро прочитать traceback?

Начните с последней строки: тип ошибки и сообщение говорят, что сломалось. Затем найдите в средней части своё имя файла и номер строки — где сломалось. Строка кода с каретками под ними покажет точного виновника.

Чем ValueError отличается от TypeError?

ValueError — операция верная, но значение неподходящее: int('двенадцать') умеет разбирать текст в число, но слова с цифрами не содержат. TypeError — сама операция не применима к таким типам: '3' + 3 пытается сложить строку с числом.

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

Почему Python останавливает программу при ошибке?

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

Что означает in <module> в traceback?

Это уровень, на котором выполнялся упавший код. in <module> — верхний уровень файла, не внутри функции. Когда авария случится в функции, вместо <module> появится её имя, а в цепочке будет несколько строк — по одной на каждый уровень вызова.

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

«Последняя строка traceback — тип ошибки и её сообщение: читать traceback начинают именно с неё.»

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

TelegramVK

Что дальше

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

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

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