Если вы скажете профессиональным программистам, что используете print() для отслеживания ошибок, готовьтесь уворачиваться от летящих в вашу сторону стульев. Есть ли смысл продираться через дебри трассировки стека с полной настройкой отладки, будучи новичком? Некоторые скажут “да”, поскольку они изначально научились это делать. Я же утверждаю: “Любые средства хороши  —  был бы результат”. 

Это совсем не значит, что вам не нужно учиться распознавать ошибки  —  речь о другом. Когда вы программируете в свое удовольствие или намереваетесь создать для кого-либо небольшие фрагменты кода, так ли необходимо проводить отладку на профессиональном уровне? Если вы занимаетесь программированием себе на радость и в свободное время, то вряд ли вам потребуется проводить “образцовую” отладку на протяжении всей вашей карьеры разработчика. 

Print()  —  быть или не быть 

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

Прежде всего посмотрим, как функция print() может помочь в отладке кода. 

print()

print() обеспечит мгновенное визуальное воплощение того, над чем вы работаете. Если вы ожидаете вполне определенный результат, то сразу же увидите его в терминале. Если он верный  —  смело продолжайте работу. Если нет  —  ищите причину. 

Рассмотрим пример с применением print():

greek_gods = ['Zeus','Poseidon','Apollo','Bob']
greek_gods_only = [god for god in greek_gods if god!='Bob']
gods = ','.join(greek_gods_only)

Как узнать, удалось ли изгнать Боба (Bob) из стана богов? 

print(gods)
Боб покинул Олимп 

А теперь, когда Боб вернулся с небес на землю, можно рассмотреть пример из реальной практики. 

Не так давно я разработал инструмент в Autodesk Maya для управления камерами. В анимационной сцене мне потребовалось составить список всех камер съемки, исключив камеры, используемые в этом редакторе по умолчанию. К счастью для нас, в Maya они уже определены (startupCamera = True), поэтому их легко найти. 

import maya.cmds as cmds

list_cameras = cmds.listCameras()
cam_list = [cam for cam in list_cameras if not cmds.camera(cam, q=True, startupCamera=True)]

for cam in cam_list:
    print(cam)
Камеры съемки лица

Как видите, первые три позиции в списке по-прежнему занимают ненужные камеры из каких-то ссылочных файлов. Теперь, воочию столкнувшись с этой ошибкой, я могу ее устранить, добавив and ‘face’ not in cam:

Прекрасно! 

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

Вывод данных с помощью print бережет наши деньги! 

Альтернативные варианты отладки 

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

Рассмотрим ряд дополнительных способов отладки кода. Надеюсь, какой-нибудь из них придется вам по душе (или вы просто продолжите работать с print).

Python Tutor

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

Возьмем, к примеру, очень продвинутую функцию. Добавим код и кликнем на Visualize Execution (визуализация выполнения).

После этого вы сможете работать с кодом, переходя от одной строки к другой. Код находится с левой стороны, а вывод данных  —  справа. 

В следующем примере показан заключительный этап программы (шаг 9). В строке 4 происходит выполнение sum_input(10,10), и справа мы видим функцию и значение, которое она возвращает. 

Python Tutor  —  это быстрый и наглядный способ проверить код.

Assert

“Assert  —  это просто логическое выражение, которое проверяет, возвращают ли условия true или false. Если значение верно, то программа никак не реагирует и переходит к следующей строке кода. Однако, если оно ложно, то программа останавливается и выбрасывает ошибку” [Источник programiz].

Перед вами простой пример использования assert

def sum_input(input_a, input_b):
    return input_a + input_b

assert sum_input(2,3) == 5
assert sum_input(2,3) != 5
AssertionError показывает, что 2+3 на самом деле равняется 5.

С помощью assert можно проверить, соответствует ли выполнение кода ожиданиям. В вышеуказанном примере вы ждете возврата значения 5, поэтому используете этот код, который возвращает True.

assert sum_input(2,3) == 5

А есть и такой код: 

assert sum_input(2,3) != 5

2 + 3 в результате всегда дает только 5, следовательно вы ожидаете ошибку, которую и получаете. Измените код следующим образом assert sum_input(2,3) != 6 или укажите любое число кроме 5, и у вас не будет ошибок. 

Поскольку именно вы инициируете эти ошибки, то их отсутствие было бы странным. Хотите почистить код  —  воспользуйтесь вот этим вариантом: 

def sum_input(input_a, input_b):
    return input_a + input_bassert sum_input(2,3) == 5
assert sum_input(2,3) != 6
Сам Усейн Болт мог бы позавидовать скорости выполнения программы

Можете создать свои собственные сообщения об ошибках:

assert sum_input(2,3) != 5, '2+3 is 5...Stop this nonsense'

Thonny

Не так давно состоялось мое первое знакомство с Thonny, и она не оставила мне выбора  —  я влюбился. Это простая IDE со встроенным отладчиком, который показывает все, что происходит в коде. Среда на удивление простая, а возможности отладки превосходят все ожидания. 

Рассмотрим следующий пример кода: программа проверяет все файлы, находящиеся в той же папке, что и программа Python. Затем она возвращает список с кортежем: (‘filename’, ‘extension’).

import os
import pathlib

def get_file_exts(directory):
    #перечисляет все файлы в директории 
    search_path = pathlib.Path(directory).iterdir()
    file_list = [file.name for file in search_path if file.is_file()]
    
    #создает новый список с кортежем, включающем имена и расширения всех файлов:
    file_ext = [os.path.splitext(file) for file in file_list]
    return file_ext


def main():
    #где находится файл:
    current_dir = os.path.dirname(os.path.realpath(__file__))
    
    #вызывает функцию для получения имен файлов и расширений, затем выводит данные.
    for file in get_file_exts(current_dir):
        print(file)

        
if __name__ == '__main__':
    main()

Thonny проинформирует вас о том, всё ли в порядке с вашим кодом. Ниже мы видим выполнение этого кода и возвращаемые им результаты: 

Редактор кода, оболочка, помощник
Успешный возврат имен файлов и расширений в папку 

Если намеренно добавить ошибку, например переименовать search_path в searchc_path, то среагирует Помощник: 

Клик по ссылке в строке 8 в Помощнике выделяет ошибку 

Помощник также снабжает нас подсказками, облегчая процесс поиска и устранения ошибок. Если мы развернем вопрос “Did you misspell it (somewhere)?”, в котором он интересуется, а правильно ли было написано имя, то Thonny покажет нам похожие варианты имен и станет очевидно, где была допущена ошибка. 

Благодарю тебя, Помощник. Теперь я знаю, где ошибка. 

Если вы запускаете режим отладки, то Thonny пошагово выполнит код и покажет все, что в нем происходит: 

Режим отладки

Он останавливается на строке 24 с инструкцией if.

Выделение области
Позволяет нам узнать условие инструкции if 
Обратим внимание на __name__
Это ‘__main__’? 
Они совпадают? 
Да. Выполняем main()

pdb  —  встроенный отладчик Python 

Если же вы предпочитаете работать в терминале, то Python оснащен встроенным отладчиком, известным как pdb. Благодаря ему вам не приходится повсюду в коде применять функции print(). Помимо этого, вы можете проверять функции и переменные и тестировать код в реальном режиме без редактирования его исходного варианта. Используя данный способ отладки, вы не ломаете код только из-за необходимости протестировать те или иные случаи. 

Просто добавив в код breakpoint(), вы укажите интерпретатору, где именно вы намерены войти в режим отладки. Если у вас отсутствует новая версия Python (3.X), то придется внести дополнительную строку import pdb; pdb.set_trace().

Более подробную информацию о pdb вы найдете в соответствующей документации

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

Заключение

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

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

Если же вы намерены и дальше использовать print()  —  я только “за”. Главное, чтобы сеньоры были не против. 

Читайте также:

Читайте нас в TelegramVK и Яндекс.Дзен


Перевод статьи Martin Andersson Aaberge: Ignore the Professionals — Debug Your Python Code Using Print()

Предыдущая статьяTDD и обработка исключений в ASP.NET Core с помощью xUnit
Следующая статьяДобавление отношений в схему GraphQL