Вы пишете операторы print() для отладки кода Python?

Я был одним из тех, кто отлаживал код с помощью операторов print(). Иногда, если код длинный, то приходится выводить больше символов, чтобы отличить один от другого.

Посмотрите на фрагмент кода ниже.
(Фрагменты кода в этом блоге соответствуют синтаксису Python 3.7)

print(style_dict,"{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{}}}}}}}}}}}}}}}}}}}}}}}}}}]]]]]]]]]]]]]]]]]]]")
# Adding into a dictionary
res_dct = {style_dict[i]: style_dict[i + 1] for i in range(0, len(style_dict), 2)}
res_dist={res_dct['Email Address']:{style_dict[i]: style_dict[i + 1] for i in range(0, len(style_dict), 2)}}
print(res_dist,"+++++++++++++++++++++++++++++++++++++++++++++++++++++")
recon_dict = res_dct
print(recon_dict,"---------------------------------------------------")
# Removing space so that data can be transferred to HTML fields
recon_dict = {x.translate({32: None}): y
              for x, y in list(recon_dict.items())}
print("##################################################")
print(recon_dict)
print("################################################")

# Converting to JSON
r = json.dumps(recon_dict)
print("$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$")
print(r)
loaded_json = json.loads(r)
print("WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWwwwwww")
print("******************************************************")
print(loaded_json)

Здесь я пытаюсь добавить словарь в файл JSON. Из-за некоторой ошибки мне пришлось использовать такое количество операторов print с различными символами для отладки.

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

Давайте посмотрим на некоторые недостатки этого подхода:

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

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

Простой оборот

Нам не нужно ничего делать, кроме как использовать мощное оружие, которое предоставляет нам Python, - "модуль pdb". Этот модуль помогает нам эффективно отлаживать.

Что такое pdb (python debugger)?

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

Некоторые способы вызова pdb:

Здесь мы рассматриваем 3 способа вызова pdb.
Postmortem: Используйте это, если вы хотите отлаживать на уровне программы.
Inline pdb: Используйте это, если вы работаете с версиями, более ранними, чем 3.7
breakpoint(): Используйте это, для версии 3.7 и выше

Postmortem

Давайте разберемся с помощью простой программы.

debug_add.py

def add_num(listA,num):
    sum=[]
    for i in listA:
        sum.append(i*num)
    return sum

listA = [2, 4, 6, 8]
num=10
result=add_num(listA,num)
print(result)

Здесь def add_num должен добавить значение переменной num к каждому элементу списка listA, сохранить новые значения в списке sum и вернуть сумму списка.

Выполнение файла python, как показано ниже, вызовет pdb,

python -m pdb debug_add.py

Это приведет к переходу в режим pdb и остановке на первой строке кода.

(venv) C:\Users\PycharmProjects\>python -m pdb debug_add.py
> c:\users\pycharmprojects\debug_add.py(2)<module>()
-> def add_num(listA,num):
(Pdb)

В любое время, если вам нужна помощь в работе с отладчиком, используйте 'h'(help), где перечислены все опции.

(Pdb) hDocumented commands (type help <topic>):
========================================
EOF    c          d        h         list      q        rv       undisplay
a      cl         debug    help      ll        quit     s        unt
alias  clear      disable  ignore    longlist  r        source   until
args   commands   display  interact  n         restart  step     up
b      condition  down     j         next      return   tbreak   w
break  cont       enable   jump      p         retval   u        whatis
bt     continue   exit     l         pp        run      unalias  whereMiscellaneous help topics:
==========================
exec  pdb

Справка по определенной опции,

(Pdb) h debug
debug code
        Enter a recursive debugger that steps through the code
        argument (which is an arbitrary expression or statement to                    
        be executed in the current environment).

Вернитесь к программе, для перехода к следующему шагу выполнения используйте опцию 'n'(next).

> c:\users\pycharmprojects\debug_add.py(2)<module>()
-> def add_num(listA,num):
(Pdb) n
> c:\users\prade\pycharmprojects\jobportal\debug_add.py(8)<module>()
-> listA = [2, 4, 6, 8]

Здесь мы можем исследовать значения переменной, задав ее имя, как показано ниже,

(Pdb) listA
*** NameError: name 'listA' is not defined
(Pdb)
*** NameError: name 'listA' is not defined

Мы дошли до строки listA = [2, 4, 6, 8], но все еще не выполнили, поэтому написано listA not defined. Если вы заметили, что если мы нажмем Enter в любое время, то предыдущий вариант будет выполнен, как описано выше.

Теперь нажмите 'n', чтобы двигаться вперед и проверить переменную listA.

(Pdb) n
> c:\users\pycharmprojects\debug_add.py(9)<module>()
-> num=10
(Pdb) listA
[2, 4, 6, 8]
(Pdb)

Чтобы проверить, в какой строке кода мы находимся, используйте опцию 'l' (line). Стрелка указывает на строку, в которой мы находимся, EOF означает конец файла.

(Pdb) l
  4         for i in listA:
  5             sum.append(i*num)
  6         return sum
  7
  8     listA = [2, 4, 6, 8]
  9  -> num=10
 10     result=add_num(listA,num)
 11     print(result)
[EOF]
(Pdb)

Для выхода из отладчика мы используем опцию 'q' (quit).

(Pdb) q(venv) C:\Users\PycharmProjects\>

Другой способ использовать метод postmortem - остановить выполнение, только когда встречается исключение, Для этого используйте -c continue вместе с -m pdb.

python -m pdb -c continue debug_add.py

Inline pdb

В ранних версиях Python 3.7 мы должны явно импортировать модуль pdb и вызвать pdb.set_trace(), чтобы остановить программу и выполнить отладку.

def add_num(listA,num):
    sum=[]
    for i in listA:
        sum.append(i*num)
    return sum

listA = [2, 4, 6, 8]
num=10
import pdb; pdb.set_trace()
result=add_num(listA,num)
print(result)

Давайте рассмотрим консоль, когда мы запускаем это.

> c:\users\pycharmprojects\debug_add.py(11)<module>()
-> result=add_num(listA,num)
(Pdb)

breakpoint()

Начиная с Python 3.7, появилось определение breakpoint(), которое помогает отлаживать питонический код без необходимости явного импорта модуля pdb и вызова pdb.set_trace(). breakpoint() делает все это за нас и открывает отладчик PDB в консоли.

Теперь давайте выполним приведенный выше код без точек останова и отладим, если возникнут ошибки.

def add_num(listA,num):
    sum=[]
    for i in listA:
        sum.append(i*num)
    return sum

listA = [2, 4, 6, 8]
num=10
result=add_num(listA,num)
print(result)

Выход:

C:\Users\PycharmProjects\venv\Scripts\python.exe C:/Users/PycharmProjects/debug_add.py
[20, 40, 60, 80]Process finished with exit code 0

Задача блока кода состоит в том, чтобы добавить число, равное 10, к каждому из элементов списка и вернуть новый список.
Ожидаемый результат - [12, 14, 16, 18]
Фактический результат - [20, 40, 60, 80]

Теперь давайте воспользуемся инструментом breakpoint() для отладки и исправления кода.

Место установки точки останова() зависит от того, где вы подозреваете наличие ошибки. В данном случае мы помещаем его перед входом в определение add_num().

def add_num(listA,num):
    sum=[]
    for i in listA:
        sum.append(i*num)
    return sum

listA = [2, 4, 6, 8]
num=10
breakpoint()
result=add_num(listA,num)
print(result)

Выход:

> c:\users\pycharmprojects\debug_add.py(11)<module>()
-> result=add_num(listA,num)
(Pdb) n
> c:\users\pycharmprojects\debug_add.py(12)<module>()
-> print(result)
(Pdb) n
[20, 40, 60, 80]
 — Return — 
> c:\users\prade\pycharmprojects\jobportal\debug_add.py(12)<module>()->None
-> print(result)
(Pdb)

Опция 'n'(next) используется для перехода к следующей строке или перешагивания через любые определения. Но в данном случае нам нужно перейти к определению, для этого мы используем опцию 's'(step).

Ниже текст, выделенный жирным шрифтом, используется для выделения используемой опции и ее объяснения.

> c:\users\prade\pycharmprojects\jobportal\debug_add.py(11)<module>()
-> result=add_num(listA,num)
(Pdb) s  <----- Step into def add_num
--Call--
> c:\users\prade\pycharmprojects\jobportal\debug_add.py(2)add_num()
-> def add_num(listA,num):
(Pdb) s  <---- stepped inside def add_num
> c:\users\prade\pycharmprojects\jobportal\debug_add.py(3)add_num()
-> sum=[]
(Pdb) n  <--- inside a def feel free to use 'n'
> c:\users\prade\pycharmprojects\jobportal\debug_add.py(4)add_num()
-> for i in listA:
(Pdb) n 
> c:\users\prade\pycharmprojects\jobportal\debug_add.py(5)add_num()
-> sum.append(i*num)
(Pdb) n
> c:\users\prade\pycharmprojects\jobportal\debug_add.py(4)add_num()
-> for i in listA:
(Pdb) sum  <-- examine sum value
[20] <--- 2+10 =12 not 20,oops we used '*'instead of '+' in      
                          appending to list sum,CAUGHT IT!
(Pdb) i  <-- so, examine i
2
(Pdb) sum.append(i+num) <-- try adding + in the expression
(Pdb) sum
[20, 12] <-- PERFECT, FIXED IT!
(Pdb) u <-- used to skip other iterations of for loop.
> c:\users\prade\pycharmprojects\jobportal\debug_add.py(11)<module>()
-> result=add_num(listA,num)
(Pdb) c <-- used to continue with execution
[20, 12, 40, 60, 80] <--not a right answer but found a fix.Process finished with exit code 0

Выше, после первой итерации цикла for, мы просмотрели значение суммы, и оно отобразило 20 вместо 12. Мы почти поймали себя на том, что ошибочно поставили *(умножение) вместо +(сложение). Тогда мы сделали шаг вперед и рассмотрели 'i', которое в этот момент равно 2, и попробовали sum.append(i+num).  Затем рассмотрение суммы дало нам 12 как недавно добавленный элемент. Следовательно, мы получили исправление, поэтому пропустили оставшиеся итерации цикла for, используя опцию 'u' (until). Затем перешли к следующему шагу после цикла. Здесь мы использовали 'c' (continue), чтобы продолжить выполнение, и оно завершилось.

Теперь исправление,

def add_num(listA,num):
    sum=[]
    for i in listA:
        sum.append(i+num)
    return sum

listA = [2, 4, 6, 8]
num=10
result=add_num(listA,num)
print(result)

Выход:

C:\Users\PycharmProjects\venv\Scripts\python.exe C:/Users/PycharmProjects/debug_add.py
[12, 14, 16, 18]Process finished with exit code 0

Отлажено!!!

Разве это не кажется простым, без беспорядочных операторов print()?

Небольшой обзор опций pdb, используемых здесь:

n - перейти к следующей строке/перешагнуть через определения
s - перейти к определениям (встроенным / определенным пользователем)
u - пропустить оставшиеся итерации в цикле
c - продолжить выполнение или пока не будет найдена следующая точка останова() <
l - показать стрелкой "->"
текущую строку кода для выполнения q - выход из отладчика
имя переменной - посмотреть текущее состояние переменной
h имя опции - отображение справки по предоставленной опции
h - для просмотра меню опций и изучения дополнительных опций по мере необходимости.

Заключение

pdb - это мощное оружие для отладки Pythonic кода, которое добавляет "эффективность", поскольку в вашем коде нет беспорядочных операторов print()и "эффективность", поскольку значительно сокращает время отладки.

С кучей предоставленных опций, это должен быть ваш инструмент "goto", когда вы столкнетесь с вашей следующей ошибкой.

Вернуться на верх