Для чего нужны новые инструменты?
Видели когда-нибудь такой вывод ошибок?
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
А хотели бы, чтобы вывод был немного более понятным и наглядным? Как показано здесь:
Или даже сделать визуализацию того, какие строки кода выполняются и сколько раз они выполняются в режиме реального времени:
Именно это помогут сделать инструменты, о которых пойдет речь дальше в статье. Вот эти три инструмента.
- Loguru — для лучшего вывода исключений.
- Snoop — печатает строки кода, выполняемого в функции.
- Heartrate — визуализирует выполнение программы на Python в режиме реального времени.
И для использования этих инструментов нужна всего одна строка кода!
Loguru — для лучшего вывода исключений
Loguru — это библиотека, делающая логирование на Python приятным. Loguru предоставляет много интересных функциональных возможностей, самая полезная из которых — возможность отлавливать неожиданные ошибки и отображать значение переменной, которое приводит к сбою кода.
Устанавливается Loguru так:
pip install loguru
Разберемся, чем так хорош Loguru. Представьте, что у вас две функции 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)
Обратите внимание: combinations([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
По этому выводу мы видим, что ошибка возникает в строке return num1/num2
, но не знаем, которое из значений (num1
или num2
) вызывает ее. К счастью, это легко отследить, добавив декоратор Loguru logger.catch
:
from loguru import logger
from itertools import combinations
def division(num1: int, num2: int):
return num1/num2
@logger.catch # Добавляем это для отслеживания ошибок
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)
Вывод:
Добавили logger.catch
, и теперь исключения гораздо проще трактовать! Оказывается, ошибка возникает при делении 2
на 0
.
Snoop — печатает строки кода, выполняемого в функции
А что, если в коде нет ошибки, но мы хотим выяснить, что в нем происходит? Здесь пригодится 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
, помогает добавление к функции factorial
декоратора snoop
:
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)}")
Вывод:
В этом выводе показываются значения переменных и то, какие строки кода выполняются. Теперь рекурсия понятна намного лучше!
Heartrate — визуализирует выполнение программы на Python в режиме реального времени
Для визуализации того, какие строки кода выполняются и сколько раз, попробуйте heartrate.
heartrate — инструмент от создателя snoop. Устанавливается heartrate так:
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. Вот такой вывод вы должны увидеть:
Темно- и светло-синие полосы показывают строки кода, которые выполнялись. Чем длиннее полосы, тем больше было выполнений, более светлые цвета соответствуют самым последним выполнениям.
По этому выводу мы видим, что программа выполняется:
if x==1
пять раз.return 1
один раз.return (x * factorial(x-1))
четыре раза.
Вывод имеет смысл, так как исходное значение x
равно 5 и функция вызывается повторно до тех пор, пока x
не станет равным 1
.
Теперь посмотрим, как визуализируется выполнение программы на Python в режиме реального времени с помощью 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)}")
Теперь мы видим, какие строки кода выполняются и сколько раз каждая из них выполнялась в режиме реального времени.
Заключение
Поздравляю! Вы только что освоили три инструмента для отслеживания и визуализации выполнения кода на Python. Надеюсь, что с ними отладка будет для вас более легкой и беспроблемной. И раз уж этим инструментам нужна всего одна строка кода, почему бы не испытать их в деле?
Смело дублируйте исходный код этой статьи и экспериментируйте здесь.
Читайте также:
- Как оптимизировать набор текста с помощью Python
- Как создать простого командного бота в Python
- 22 сниппета на Python для повседневных задач
Читайте нас в Telegram, VK и Яндекс.Дзен
Перевод статьи Khuyen Tran: 3 Tools to Track and Visualize the Execution of your Python Code