Python Traceback — Как правильно исправлять ошибки в коде

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

Понятие трассировки и зачем она нужна?

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

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

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

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

def say_hello(man):

    print('Привет, ' + wrong_variable)

 

say_hello('Иван')

Здесь say_hello() вызывается с параметром man. Тем не менее, в say_hello() данное имя переменной не используется. Причиной тому служит то, что оно написано по-другому: wrong_variable в вызове print().

Учтите то, что вы этой статье подразумевается то, что вы уже имеете представление о том, что такое ошибки Python. Если вы основы обработки ошибок не знаете, то часть сегодняшнего текста вам может быть непонятной.

После запуска этого приложения будет получена следующая трассировка. 

Traceback (most recent call last):

  File "/home/test.py", line 4, in <module>

    say_hello('Иван')

  File "/home/test.py", line 2, in say_hello

    print('Привет, ' + wrong_variable)

NameError: name 'wrong_variable' is not defined

 

Process finished with exit code 1

Это выдача, в которой есть большое количество данных, необходимых для анализа причин той ошибки, которая возникла. В последней строчке traceback мы понимаем тип исключения. В том числе, предоставляются дополнительные релевантные сведения. 

Прошлые строчки из traceback говорят, какой конкретно код вызвал это исключение.

В traceback, бывшем ранее, исключение NameError. Она говорит о том, что есть отсылка к определенному имени, не определенного до того момента, как исполнялся этот код. В нашем случае ссылка осуществляется на wrong_variable.

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

Как понять причины ошибки?

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

Структура трассировки: особенности

Каждая трассировка имеет универсальные черты. Но все секции вывода являются важными. В этом изображении описаны несколько из них.

Рекомендация: знакомиться с трассировкой рекомендуется в последовательности снизу вверх.

  1. Поле синего цвета. Последняя строчка из трассировки – уведомление об исключении, появившемся в процессе исполнения программы. Конкретно в этом поле указывается ее название.
  2. Зеленое поле. В него включено описание ошибки. Это описание, как правило, включает полезные сведения для понимания, по какой причине эта ошибка возникла. 
  3. Поле желтого цвета. Немного ранее в трассировке хранятся сведения о тех функциях, которые вызывались в программе. Они идут по направлению к верху – от самых поздних до тех, которые были раньше всего. Такие вызовы являют собой двухстрочные вводы для всех вызовов. Причем первая строчка всех вызовов включает такие данные, как имя того файла, в котором появилась ошибка, номер строки, название модуля. Каждая из этих деталей позволяет понять, в каком конкретно месте может быть найден код.
  4. Красное подчеркивание. Вторая строка данных вызовов включает непосредственно тот код, при выполнении которого было вызвано исключение.

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

Python 3.7.4 (default, Jul 16 2019, 07:12:58) 

[GCC 9.1.0] on linux

Type "help", "copyright", "credits" or "license" for more information.

>>> 

>>> 

>>> def say_hello(man):

...     print('Привет, ' + wrong_variable)

... 

>>> say_hello('Иван')

 

 

Traceback (most recent call last):

  File "<stdin>", line 1, in <module>

  File "<stdin>", line 2, in say_hello

NameError: name 'wrong_variable' is not defined

Учтите то, что на месте имени файла обнаружите <stdin>. В этом нет ничего странного, так как вы выполнили код через стандартный ввод. Также в трассировке не показываются выполненные строки.

Важно учитывать то, что если вам известны трассировки в иных языках разработки приложения, то увидите различия с таковыми в Python. Дело в том, что подавляющее количество языков разработки показывают ошибку в начале, и идут по направлению вниз. В Python же система другая.

Почему было сделано так, чтобы трассировка шла вверх? Дело в том, что трассировка завершается в конце выдачи. Это облегчает структурирование прочтение вывода и разобраться, в чем ошибка.

Примеры кода с выводом трассировки

Изучение конкретных примеров трассировок позволяет лучше разобраться в том, какие сведения в них представлены и как их применять.

Код, который мы покажем ниже, применяется в примерах для иллюстрации данных, которые предоставляются в трассировке.

Мы выполнили код, который ниже, в роли примера. Покажем, какие сведения нам помогла получить трассировка.

Давайте сохраним этот код в файле greetings.py

def who_to_greet(person):

    return person if person else input('Кого приветствовать? ')

 

def greet(someone, greeting='Здравствуйте'):

    print(greeting + ', ' + who_to_greet(someone))

 

def greet_many(people):

    for person in people:

        try:

            greet(person)

        except Exception:

            print('Привет, ' + person)

Функция who_to_greet() принимает значение person и либо возвращает это значение в случае, если оно содержит хоть какие-то значения, либо просит пользователя ввести значение с помощью input().

Затем, greet() применяет имя для приветствия из someone, необязательное значение из greeting и вызывает print(). Также со значением, которое берется из someone вызывается функция who_to_greet().

И, в результате, greet_many() осуществляет итерацию по списку людей и выполняет greet(). Если при вызове этой функции случается ошибка, то тогда на вывод идет резервное приветствие print(‘hi, ‘ + person).

Вообще, если ввод верный, ошибок при выполнении этого кода не может быть. Ведь при его написании ошибок не было допущено.

Если выполните greet() в конце кода (который был сохранен в файле greetings.py) и дадите аргумент, которого в данной программе не может быть, то трассировка получится следующей. 

$ python greetings.py




Traceback (most recent call last):

  File "/home/greetings.py", line 19, in <module>

    greet('Chad', greting='Yo')

TypeError: greet() got an unexpected keyword argument 'greting'

Такой вывод переводится, что функция greet получила ключевой аргумент ‘greting’, который для  интерпретатора оказался неожиданным.

Еще раз, если говорить о трассировке Python, рекомендуется проводить анализ по в сторону снизу вверх. По ходу того, как вы будете двигаться выше, вы обнаружите строку, которая и привела к такой ошибке. 

Далее мы увидим место нахождения файла, где записан код нашего приложения, номер его строки и то, какой модуль подключен. 

Видим, что анализировать ошибки в Python не так и трудно, не так ли?

Обзор основных исключений

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

Проанализируем основные исключения, с которыми придется работать.

AttributeError object has no attribute

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

Вот пример кода, вследствие выполнения какого программа сообщила о наличии проблемы. 

>>> an_int = 1

>>> an_int.an_attribute

 

Traceback (most recent call last):

  File "<stdin>", line 1, in <module>

AttributeError: 'int' object has no attribute 'an_attribute'

Строка уведомления об исключении для AttributeError указывает на то, что указанный тип объекта не обладает доступом к атрибуту. В нашем примере в качестве этого атрибута выступает an_attribute.

ImportError: No module named

Если вы задействуете оператор import, и в процессе этого возникают сложности, появляется ошибка ImportError. Сюда же может быть включенным подкласс ошибки ModuleNotFoundError. Она появляется в случае, если модуль, импортируемый в данной ситуации, не получается найти или если разработчик пытается импортировать то, что в этом модуле отсутствует. 

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

IndexError: list index out of range

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

Вот пример кода, приводящего к возникновению данного исключения. 

>>> a_list = ['a', 'b']

>>> a_list[3]

 

Traceback (most recent call last):

  File "<stdin>", line 1, in <module>

IndexError: list index out of range

Строка сообщения не предоставляет всей необходимых данных. Вы можете видеть, что у вас есть отсылка к последовательности, которая не доступна и то, какой ее тип рассматривается. В нашем случае таковой является список.

Проще говоря, в списке a_list значение с ключом 3 отсутствует. Есть лишь значение с ключами 0 и 1. Им соответствуют буквы a и b.

KeyError

Точно так же, как и в предыдущем варианте, это исключение появляется в случае, когда вы пробуете работать с ключом, который отсутствует в отображении. Как правило, речь идет о словаре. 

Проще говоря, эта ошибка является аналогом IndexError, только для словарей. 

Вот пример кода, который демонстрирует это исключение. 

>>> a_dict = ['a': 1, 'w': '2']

>>> a_dict['b']

 

Traceback (most recent call last):

  File "<stdin>", line 1, in <module>

KeyError: 'b'

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

SyntaxError

Еще один пример важного исключения, которое появляется, если в синтаксисе возникла определенная ошибка.  

>>> def greet(person)

  File "<stdin>", line 1

    def greet(person)

                    ^

SyntaxError: invalid syntax

Например, здесь ошибка возникла из-за того, что в коде не было двоеточия, которое должно быть расположено в конце строки для функции.

ОфисГуру
Adblock
detector