Забудьте про print для отладки - знакомимся с PySnooper
Давайте честно: все мы так делали. Код ведет себя странно, и вот уже руки сами тянутся написать print("я здесь"), print(f"переменная a = {a}"), чтобы понять, что вообще происходит внутри. Пять минут, десять, и вот уже ваш код усеян временными "костылями", которые потом еще и удалять придется. А если речь о сложном цикле или рекурсии, то консоль превращается в хаос.
Знакомая ситуация? А что, если я скажу,-что есть способ лучше? Способ, который не требует настройки полноценного отладчика вроде pdb, но дает гораздо больше информации, чем старый добрый print. Сегодня мы посмотрим на PySnooper — маленькую, но невероятно полезную библиотеку, которая может навсегда изменить ваш подход к быстрой отладке.
Что такое PySnooper и кому он нужен?
PySnooper — это то, что автор называет "отладчиком для бедных". Если вы когда-нибудь работали с Bash, то это как set -x для Python, только гораздо нагляднее и удобнее.
Идея проста до гениальности: вместо того чтобы вручную расставлять print для отслеживания значений переменных, вы просто добавляете один декоратор к нужной функции. И всё! PySnooper берет на себя всю грязную работу и выводит подробный лог выполнения этой функции: какая строка сейчас работает, какие переменные изменились и какие значения они приняли.
Это идеальный инструмент для случаев, когда:
- Нужно быстро разобраться в логике конкретной функции, не запуская тяжеловесный отладчик.
- Вы работаете с чужим или старым кодом и хотите понять его поведение.
- Отлаживаете сложный алгоритм и хотите видеть пошаговое изменение данных.
- Вам просто лень настраивать breakpoints и watches в вашей IDE.
Как это работает на практике?
Магия PySnooper заключается в его простоте. Давайте посмотрим на классический пример из документации: функция для преобразования числа в двоичный вид.
Магия одного декоратора
Представим, что у нас есть такая функция:
import pysnooper
@pysnooper.snoop()
def number_to_bits(number):
if number:
bits = []
while number:
number, remainder = divmod(number, 2)
bits.insert(0, remainder)
return bits
else:
return [0]
number_to_bits(6)
Мы просто добавили @pysnooper.snoop() над определением функции. Теперь при её вызове мы получим в консоли вот такой подробный отчет:

Что мы здесь видим?
- Время выполнения каждой строки с точностью до микросекунд.
- Номер строки и ее исходный код.
- Появление новых переменных (
New var) и их начальные значения. - Изменение существующих переменных (
Modified var) и их новые значения.
Больше не нужно гадать, какое значение было у number на третьей итерации цикла. PySnooper показывает все как на ладони.
Точечная отладка с with
Иногда нет нужды отслеживать всю функцию целиком. Проблема может быть в небольшом, конкретном блоке кода. И здесь PySnooper тоже не подводит. Его можно использовать как контекстный менеджер:
import pysnooper
import random
def foo():
lst = []
for i in range(10):
lst.append(random.randrange(1, 1000))
# Нас интересует только этот блок
with pysnooper.snoop():
lower = min(lst)
upper = max(lst)
mid = (lower + upper) / 2
print(lower, mid, upper)
foo()
В этом случае лог будет сгенерирован только для кода внутри блока with. Это очень удобно, чтобы не засорять вывод лишней информацией.
Расширенные возможности: что еще умеет PySnooper?
Простота — это главное, но не единственное достоинство PySnooper. У него есть несколько полезных параметров для более тонкой настройки.
1. Вывод в файл
Если вы работаете в среде, где доступ к stderr затруднен (например, в некоторых веб-фреймворках или при запуске скриптов в CI/CD), логи можно легко перенаправить в файл:
@pysnooper.snoop('/var/logs/my_debug.log')
def my_complex_function():
# ...
2. Отслеживание выражений
Иногда нужно следить не за локальной переменной, а за значением какого-нибудь выражения, например, атрибута объекта или элемента словаря. Для этого есть параметр watch:
@pysnooper.snoop(watch=('self.request.user', 'config["SECRET_KEY"]'))
def process_request(self, config):
# ...
3. Погружение вглубь
По умолчанию PySnooper отслеживает только ту функцию, на которой висит декоратор. Но что, если она вызывает другие ваши функции, и вы хотите видеть, что происходит и в них? Параметр depth решает эту задачу:
@pysnooper.snoop(depth=2)
def main_function():
# ...
result = helper_function() # PySnooper заглянет и сюда
# ...
С depth=2 вы увидите лог выполнения не только main_function, но и helper_function, которую она вызывает.
Установка и начало работы
Начать пользоваться PySnooper проще простого. Он устанавливается одной командой через pip:
$ pip install pysnooper
Импортируйте, добавьте декоратор — и вы готовы к отладке нового уровня.
Выводы: ваш новый любимый инструмент?
PySnooper, конечно, не заменит полноценные отладчики вроде pdb или встроенные в PyCharm и VS Code инструменты для сложных сценариев. Его сила в другом — в скорости и простоте для решения 80% повседневных задач отладки.
Кому точно стоит попробовать PySnooper:
- Всем, кто устал от
print(): Это очевидно. PySnooper — этоprint()на стероидах, который не нужно потом удалять. - Дата-сайентистам и аналитикам: В Jupyter Notebooks, где полноценная отладка бывает громоздкой, PySnooper — просто находка для анализа шагов обработки данных.
- Начинающим разработчикам: Он отлично помогает визуализировать поток выполнения программы и понять, как меняются данные на каждом шаге.
В следующий раз, когда ваша рука потянется написать print для поиска ошибки, остановитесь. Дайте шанс PySnooper — возможно, вы удивитесь, насколько проще и быстрее может быть отладка.
