eFusion 07 мая 2021

🐍 3 инструмента для отслеживания и визуализации выполнения кода на Python

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

Перевод публикуется с сокращениями, автор оригинальной статьи Khuyen Tran.

Мотивация

Вы наверняка видели вывод ошибок наподобие показанного ниже:

        2 divided by 1 is equal to 2.0.
Traceback (most recent call last):
  File "loguru_example.py", line 17, in <module>
    divide_numbers(num_list)
  File "loguru_example.py", line 11, in divide_numbers
    res = division(num1, num2)
  File "loguru_example.py", line 5, in division
    return num1/num2
ZeroDivisionError: division by zero
    

и хотели, чтобы его было немного легче понять:

🐍 3 инструмента для отслеживания и визуализации выполнения кода на Python

Возможно, вам даже захочется визуализировать, какие строки кода выполняются и сколько раз:

🐍 3 инструмента для отслеживания и визуализации выполнения кода на Python

Если так, статья даст необходимые инструменты, чтобы достичь цели:

  • Loguru – вывод сработавших исключений;
  • snoop – печать строк кода, выполняемого в функции;
  • heartrate – визуализация выполнения программы в режиме реального времени.

Все, что нужно для использования этих инструментов – одна строка кода!

Loguru

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

Установить Loguru можно одной командой:

        pip install loguru
    

Чтобы понять, чем Loguru может быть вам полезна, представьте, что есть 2 функции – division и divide_numbers, при этом функция divide_numbers сейчас выполняется.

        from itertools import combinations

def division(num1: int, num2: int):
    return num1/num2

def divide_numbers(num_list: list):
  """Division of 2 numbers in the number list """
  
    for comb in combinations(num_list, 2):
        num1, num2 = comb 
        res = division(num1, num2)
        print(f"{num1} divided by {num2} is equal to {res}.")


if __name__ =='__main__':
    num_list = [2, 1, 0]
    divide_numbers(num_list)
    

Обратите внимание, что комбинации ([2,1,0], 2) возвращают [(2, 1), (2, 0), (1, 0)]. После выполнения приведенного выше кода мы получим следующую ошибку:

        2 divided by 1 is equal to 2.0.
Traceback (most recent call last):
  File "loguru_example.py", line 17, in <module>
    divide_numbers(num_list)
  File "loguru_example.py", line 11, in divide_numbers
    res = division(num1, num2)
  File "loguru_example.py", line 5, in division
    return num1/num2
ZeroDivisionError: division by zero
    

Из выходных данных становится ясно, что возвращающая num1/num2 строка является местом возникновения ошибки, но непонятно, какие значения num1 и num2 ее вызывают. Это можно легко отследить, добавив декоратор Loguru.catch:

        from loguru import logger 
from itertools import combinations

def division(num1: int, num2: int):
    return num1/num2

@logger.catch # Add this to track errors
def divide_numbers(num_list: list):
    for comb in combinations(num_list, 2):
        num1, num2 = comb 
        res = division(num1, num2)
        print(f"{num1} divided by {num2} is equal to {res}.")


if __name__ =='__main__':
    num_list = [2, 1, 0]
    divide_numbers(num_list)
    

Вывод:

🐍 3 инструмента для отслеживания и визуализации выполнения кода на Python

Добавив logger.catch исключения гораздо проще понять: ошибка возникает при делении 2 на 0.

Snoop

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

Чтобы установить snoop, введите следующую команду:

        pip install snoop
    

Представим, что у нас есть функция под названием factorial, которая находит факториал целого числа:

        import snoop 

def factorial(x: int):
    if x == 1:
        return 1
    else:
        return (x * factorial(x-1))
        
if __name__ == "__main__":
    num = 5
    print(f"The factorial of {num} is {factorial(num)}")
    

Вывод:

        The factorial of 5 is 120
    

Чтобы понять, почему результат factorial(5) равен 20, добавим декоратор snoop в функцию factorial.

        import snoop 

@snoop
def factorial(x):
    if x == 1:
        return 1
    else:
        return (x * factorial(x-1))


if __name__ == "__main__":
    num = 5
    print(f"The factorial of {num} is {factorial(num)}")
    

Вывод:

🐍 3 инструмента для отслеживания и визуализации выполнения кода на Python

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

Heartrate

Если необходимо визуализировать, какие строки и сколько раз выполняются, попробуйте heartrate.

Heartrate также создан разработчиками snoop. Чтобы его установить, введите команду:

        pip install heartrate
    

Теперь добавим heartrate.trace(browser=True) в предыдущий код. Это откроет отображающее визуализацию файла окно браузера, в котором была вызвана функция trace():

        import heartrate 
heartrate.trace(browser=True)

def factorial(x):
    if x == 1:
        return 1
    else:
        return (x * factorial(x-1))


if __name__ == "__main__":
    num = 5
    print(f"The factorial of {num} is {factorial(num)}")
    

При запуске приведенного выше кода должен открыться новый браузер. Если нет, перейдите на http://localhost:9999 – вы должны увидеть показанный ниже результат:

🐍 3 инструмента для отслеживания и визуализации выполнения кода на Python

Диаграммы подсвечивают искомые строки: более длинные полосы означают больше попаданий, а светлые цвета – более свежее изменение.

На приведенной выше иллюстрации мы видим, как выполняется код:

  • if x==1 (5 раз)
  • return 1 (1 раз)
  • return (x * factorial(x-1)) (4 раза)

Вывод имеет смысл, так как начальное значение x равно 5, и функция вызывается повторно до тех пор, пока x не станет равным 1.

Теперь посмотрим, как визуализировать выполнение в режиме реального времени с помощью heartrate. Добавим sleep(0.5), чтобы программа работала немного медленнее, и увеличим num до 20.

        import heartrate 
from time import sleep

heartrate.trace(browser=True)


def factorial(x):
    if x == 1:
        return 1
    else:
        sleep(0.5)
        return (x * factorial(x-1))


if __name__ == "__main__":
    num = 20
    print(f"The factorial of {num} is {factorial(num)}")
    
🐍 3 инструмента для отслеживания и визуализации выполнения кода на Python

Теперь мы можем в режиме реального времени видеть, какие строки кода выполняются, и сколько раз отработала каждая из них.

Заключение

Мы изучили три инструмента для отслеживания и визуализации выполнения кода в Python. Надеемся, что с ними отладка станет для вас менее болезненной. Поскольку эти инструменты требуют только одной строки кода, почему бы не попробовать их в работе? Удачи в обучении!

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

Источники

Комментарии

ВАКАНСИИ

Добавить вакансию
Разработчик C++
Москва, по итогам собеседования

ЛУЧШИЕ СТАТЬИ ПО ТЕМЕ