At some point in our lecture, the code we wrote started to get nested and more complicated. There were loops, functions, etc. Writing such code inevitably comes with bugs that we need to debug before we can actually use the program. It is often helpful to assemble your complicated program step-by-step and test each unit before trying to run all the code at once. However, sometimes this is exactly what you did, but there is still a bug somewhere. You can go back to the strategies you learned in Workshop 1 to find the bug (i.e., reading, running, ruminating, retreating). Here is another suggestion for a tool that will help you find your bug interactively: the Python Debugger (pdb), an interactive debugging tool that comes built-in with Python. It allows you to set breakpoints, which will stop code execution at the scope of the breakpoint so that you can interactively inspect variables and step through code to find your bug. Here’s a quick tutorial to get you started:
Starting the debugger
There are several ways to start pdb:
import pdb and then pdb.set_trace() in your script where you want to start debugging. When the python interpreter hits this line during execution, it will pause and enter the debugging mode.
or if your script crashes, you can load pdb after the fact to inspect the state at the time of the crash: pdb.pm() (for post-mortem).
I personally prefer the first approach for maximum control.
Basic commands while debugging
Once in pdb, you can use various commands to navigate and inspect your code:
l (list): Shows the current location in the file with some context.
n (next): Execute the next line of code.
s (step): Step into functions called at the current line.
c (continue): Continue normal execution until the next breakpoint.
b (break): Set a breakpoint. For example, b 15 sets a breakpoint at line 15.
p <variable> (print): Print the value of an expression. For example, p my_var prints the value of my_var.
q (quit): Exit from the debugger and end the program.
Your turn
#11-01: The character count function
Let’s go back to one of the functions we coded during this course:
The character count function
import pdbdef count_character(statement, character):""" Count the occurrences of a specific character in a given statement. Parameters: ----------- statement : str The word or sentence in which to search for the character. character : str The character to count. Must be a single character. Returns: -------- int The number of times the character appears in the statement. Raises: ------- ValueError If `character` is not a single character. Examples: --------- >>> count_character("energy efficiency", "e") 4 >>> count_character("hello world", "o") 2 >>> count_character("Python programming", "m") 2 """iflen(character) !=1:raiseValueError("The `character` argument must be a single character.") count =0for char in statement: pdb.set_trace()if char == character: count +=1return countcount_character("hello world", "o")
Put the code above in a script and execute it. Look out for where I placed the breakpoint (i.e., pdb.set_trace()). Experiment with the pdb commands listed above to explore what the debugger enables you to do. You can also move the breakpoint into the if-statement. What does it change?
After you have done that, repeat the same steps, but instead of placing the code in a script, use a notebook. Does it still work?
#11-02: Remember namespaces and scopes
Check out the examples from the section on namespaces and scopes one more time. They offer an interesting playground to explore the capabilities of pdb, see below:
import pdba ='global value'def outer(): a ='enclosed value'def inner(): a ='local value'print(a) pdb.set_trace() inner()outer()