Один из наиболее достоверных способов убедиться в том, что человек разбирается в программировании — посмотреть на то, как он отлаживает программу, то есть анализирует возникающие ошибки и устраняет их. Навык отладки (дебага, debug) не появляется сам по себе, его необходимо развивать и начинать это делать нужно как можно раньше. Этому весьма способствует настройка локальной среды разработки и повторение всего того, что делается в курсах, у себя на компьютере. Следующие курсы как раз помогают проделать эти шаги.

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

Traceback (most recent call last):
  File "users.py", line 4, in <module>
    main()
  File "users.py", line 2, in main
    create()
NameError: name 'create' is not defined

Вывод ошибок делится на две части: непосредственно сообщение об ошибке и трейсбэк (traceback). Traceback — это список всех вызовов функций от запуска программы вплоть до того места, где произошла ошибка. Трейсбэк — очень важный инструмент, который позволяет увидеть то, как выполнялась ваша программа и какие функции вызывались. Отладка всегда сводится к двум вещам:

  1. перевести сообщение об ошибке
  2. найти в трейсбэке то место в своем коде, в котором произошла ошибка.

Каждая запись в трейсбэке представляет собой указание на файл и строчку, в которой была вызвана соответствующая функция. В рамках одного трейсбэка возможны (и часто встречаются) ситуации, когда часть функций вызывается где-то в библиотеках, которые вы не писали, но используете, а часть — в вашем коде.

Типы ошибок

Наиболее простые и понятные ошибки — синтаксические. Они связаны исключительно с тем, что код записан неверно, например, забыта точка с запятой в конце инструкции. В выводе таких ошибок всегда присутствует фраза "SyntaxError: …". Для их исправления нужно открыть то место в коде, на которое указывает ошибка, и внимательно на него посмотреть.

Traceback (most recent call last):
  File "users.py", line 2
    print("Hello" + "world')
                           ^
SyntaxError: EOL while scanning string literal

Еще одна большая группа ошибок называется ошибками программирования. К ним, например, относятся:

  • вызов несуществующей функции
  • использование необъявленной переменной
  • передача неверных аргументов в функции, например, аргументов, имеющих неверный тип

Эти ошибки исправить труднее, чем синтаксические. Обычно они возникают в результате неправильной логики в другом, более раннем вызове.

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

# Функция должна считать сумму чисел, но считает разность:
def sum(a, b):
    return a - b

Отладка

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

Рассмотрим конкретный пример. Ниже описана функция, которая считает сумму чисел от числа start до числа finish. Если начало равно трём, а конец — пяти, то программа должна вычислить: 3 + 4 + 5.

def sum_of_series(start, finish):
    result = 0
    n = start
    while n < finish:
        result += n
        n += 1
    return result

В этом коде допущена ошибка. Вы её видите? Если очень постараться, ошибку можно заметить, но на это никогда не стоит надеяться. Новички часто думают, что они невнимательны, и очень расстраиваются, когда допускают такие ошибки. Хочу вас успокоить: опытные разработчики допускают такие ошибки не реже новичков. Важно не то, что вы их допускаете, а то, что вы способны отладить этот код (и сделать это быстро). Этим отличаются опытные разработчики от начинающих. Никогда не пытайтесь найти ошибку с помощью медитации над кодом, сверля его взглядом. Если быстрая проверка не дала ответа, то приступайте к отладке.

На Хекслете, в обсуждении уроков, нам пишут: "этот код не работает", и показывают свой код. Вероятно, начинающие разработчики думают, что опытные могут понять, в чем ошибка и найти ее, просто посмотрев на код, но это совсем не так. Глядя на такой код, невозможно понять, а что, собственно, пошло не так, и я уже не говорю про нахождение самой ошибки. Нам также нужно увидеть сообщение об ошибке и начать отладку.

Глядя на код функции sum_of_series замечаем, что основных переменных там две: n и result, именно они меняются в цикле. Из этого можно сделать ровно один вывод: нужно явно посмотреть, какие значения им даются на каждой итерации. После этого найти ошибку не составит труда.

Один из способов отслеживать значения переменных во время выполнения кода связан с использованием специальных программ-отладчиков. Отладчики интегрируются с популярными редакторами и позволяют визуально выполнить код по шагам, отслеживая любые изменения. Подробнее о том, как их использовать можно прочитать во множестве статей (гуглить "python pdb").

Python debugging

В среде Хекслета отладчика нет, поэтому здесь используется другой подход (но выполняющий ту же задачу) — отладочная печать. Суть такая же, как и в визуальном отладчике, но для вывода значения переменных используется обычная печать на экран:

def sum_of_series(start, finish):
    result = 0
    n = start
    while n < finish:
        print('new iteration !!!!')
        print(n)
        result += n
        n += 1
        print(result)
    return result

sum_of_series(3, 5)

# new iteration !!!!
# 3
# 3
# new iteration !!!!
# 4
# 7

То что печатается на экран, отображается во вкладке OUTPUT, на которую автоматически переключается редактор во время проверки. Из этого вывода сразу можно понять, что итераций цикла на одну меньше, чем нужно. Почему-то не выполняется сложение для последнего числа, которое обозначено как finish. И действительно, если посмотреть на определение, то видно, что там используется n < finish вместо n <= finish.


Дополнительные материалы

  1. Как найти ошибки в коде?
  2. pdb — The Python Debugger

Для продолжения нужно перейти в курс и вступить в него.