Забудьте про print для отладки - знакомимся с PySnooper

08 Jun, 2026
16,587
🔱 958
👥 228

Давайте честно: все мы так делали. Код ведет себя странно, и вот уже руки сами тянутся написать 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 — возможно, вы удивитесь, насколько проще и быстрее может быть отладка.

🍪 Мы используем файлы cookie и сервис аналитики Яндекс.Метрика, чтобы сайт работал лучше. Продолжая пользоваться devtrends.ru, вы соглашаетесь с обработкой данных согласно Политике конфиденциальности.