What to do when you don't know what to do: the interpreter, the debugger, and some useful resources
Topics:
Python interpreter, help() and dir()
iPython interpreter (and the SciPy SuperPack!)
iPython magic functions
iPython configuration, history, saving settings and history
The Python Debugger: PDB
How to use the debugger within scripts and within the interpretter
Using the debugger with exceptions
Today You Will:
Learn to use Python Interpreters and the Python Debugger module to explore your programs and get out of jams.
The interpreter lets you interact with Python: as opposed to writing a script that it executed all at once, the interpreter allows you to step through the code piece by piece as you enter it. This can be a big help in both exploring how you should implement an algorithm in the first place and also in troubleshooting code that's just not doing what you thought it was going to do.
Running the default Python Interpreter
We'll start with the basic Python Interpreter, then we'll move on to a fancier-pants'd interpreter in a bit.
To launch the interpreter, simply call python with no script name after it:
$ python
Python 2.5.1 (r251:54863, Feb 62009,19:02:12)[GCC 4.0.1 (Apple Inc. build5465)] on darwin
Type "help","copyright","credits"or"license"for more information.
>>>
The >>> is the prompt for you to enter commands at, so I'll use it from here on out if I mean for you to enter something into the interpreter (as opposed to the command line).
Let's start off by assigning a value to a variable.
>>> people =['Rich','Matt','Terry']
And now if we type the name of the variables, "people", we will see the value assigned to it:
>>> people
['Rich','Matt','Terry']
We can manipulate the variables and get the feedback instantly:
>>> people * 3['Rich','Matt','Terry','Rich','Matt','Terry','Rich','Matt','Terry']
Note that the last command didn't change the variable, it just showed us the output of multiplying the variable by 3 (which, for a list, means to repeat its contents 3 times).
The interpreter has a couple of resident functions available to help us along in this interactive endeavor. The first you should know is help().
>>>help()
Welcome to Python 2.5! This is the online help utility.
If this is your first time using Python, you should definitely check out
the tutorial on the Internet at http://www.python.org/doc/tut/.
Enter the name of any module,keyword,or topic to get help on writing
Python programs and using Python modules. To quit this help utility andreturn to the interpreter, just type"quit".
To get a list of available modules, keywords,or topics,type"modules","keywords",or"topics". Each module also comes with a one-line summary
of what it does; to list the modules whose summaries contain a given word
such as"spam",type"modules spam".
help>
As the help function has just informed us, you can use help() to get help on specific modules, keywords, or topics. Let's use it to get the low-down on the other very useful interpreter function, dir().
help>dir
Help on built-in function dirin module __builtin__:
dir(...)dir([object]) ->list of strings
Return an alphabetized list of names comprising (some of) the attributes
of the given object,and of attributes reachable from it:
No argument: the names in the current scope.
Moduleobject: the module attributes.
Typeorclassobject: its attributes,and recursively the attributes of
its bases.
Otherwise: its attributes, its class's attributes, and recursively the
attributes of its class's base classes.
(END)
This shows us that dir() can tell us about objects in our namespace, which in non-jargon means that dir() can tell us about things like variables and modules and functions, and which ones we have access to at whatever point we're at in our program.
This command shows us ALL the things that belong to our list variable, including the methods that we can call to manipulate it, such as .append(), etc. It also shows us a whole bunch of internal objects that belong to our list variable (which don't belong to us, and we don't get to use them -- at least not in this class).
So, hopefully you can see how to use this power for good: dir() can tell you all the methods and functions available to an object. For those of us that prefer to work in the interpreter, we find ourselves only rarely consulting exterior documentation. We can use dir() to find out what is available to an object, and then we can use help() to figure out what things are:
>>>help(people.append)
Help on built-in function append:
append(...)
L.append(object) -- append object to end
(END)
Which very succinctly tells us what we've already learned: people.append() will add whatever object we put in the parentheses to the end of the people list.
Using iPython from the SciPy Superpack
The default Python interpretter is a very basic interface. There are things that you'd like to be able to do, such as enter more than one line of code at a time, or perhaps look back through your history of commands. The enhanced iPython interpreter provides these perks and more.
You can start off by launching iPython from the command line:
$ ipython
Python 2.5.1 (r251:54863, Feb 62009,19:02:12)
Type "copyright","credits"or"license"for more information.
IPython 0.8.3.svn.r3001 -- An enhanced Interactive Python.
? -> Introduction and overview of IPython's features.
%quickref -> Quick reference.
help -> Python's own help system.
object? -> Details about 'object'. ?object also works, ?? prints more.
In[1]:
iPython launches and tells us what version we're running, and gives us a couple of tips. Just typing a question mark and pressing enter will get us a comprehensive introduction to iPython, which I'll leave to you to peruse in your leisure time. You'll notice that the prompt for iPython includes a line number for each line of your code, signified on the first line by ln [1]:. The %quickref guide gives us a succint description of the fanciness of iPython. For starters, you have instant access to the system prompt for commands like "ls" and "cd."
In [19]: ls
S1.2/ missedcons
S4.2/ pc_analysis/
S5.2/ playtime/
S6.2/ probes/
S8.1/ scratch.py
In [21]: cd S6.2
/Users/matt/S6.2
A number of "magic" functions, all of which start with a percent sign "%", provide functionality that the regular interpretter cannot provide. One for regular use is %history, which will show you the lines of code you have previously entered in this session:
And if you look closely at that output, you might correctly suspect that you can also use %hist as a shortcut. This can be really helpful for repeating a block of code a couple of times, in which case, you might not want the line numbers getting in the way of your cutting-and-pasting the code back to the command line. Check out this simple example of a for loop printing our characters from a string, then viewing the previous commands:
In [10]: mystr ='ajskdlflsd'
In [11]: for i in mystr:
....: print i
....:
....:
a
j
s
k
d
l
f
l
s
d
In [12]: %hist -n
_ip.system("ls -F ")
_ip.magic("cd S6.2/")
_ip.magic("hist -n")
_ip.system("ls -F ")
mystr ='askldjfas'
_ip.magic("hist ")
mystr ='ajskdlflsd'for i in mystr:
print i
Notice that all the "magic" or "system" commands like %history and "ls" register with an _ip.magic or _ip.system function prefix around them. But the lines of actual python code appear in order, and formatted, such that you could easily copy them back to the prompt again, or to a text editor. Or, alternatively, you can use the %save command to capture the desired subset of your history and send to a textfile:
In [11]: hist
1 : _ip.magic("cpaste ")2 : _ip.magic("cd S6.2; edit tes")3 : _ip.system("ls -F ")4 : _ip.magic("cd S6.2")5 : _ip.magic("edit test.txt.py")6 : _ip.magic("edit text.txt.py")7 : _ip.magic("hist -n")8 : mystr ='ajskdlflsd'9 :
for i in mystr:
print i
In [12]: %save histfile.py8-9
The following commands were written to file `histfile.py`:
mystr ='ajskdlflsd'for i in mystr:
print i
This saves lines 8 and 9 of the history (our little for loop) into a separate file. Now, if we want to change something up, we can edit the file with the %edit command, and when we exit with our editor, the file will run again.
In [13]: %edit histfile.py
done. Executing edited code...
a a
j j
s s
k k
d d
l l
f f
l l
s s
d d
Okay, so there are lots of nice little magic tricks in iPython, and you can use teh %quickref guide to explore them more on your own. Meanwhile, lemme show you a couple of other handy tricks that don't involve magic functions.
Remember in the basic Python interpretter, we could use dir() to find out what objects belonged to a given object? Well, in iPython, all we have to do to capture the same information is type the object and period, then press the tab key twice.
This shows us all the things we can use or modify that belong to our variable "mystr", from our for loop.
And what if you don't know what one of those functons or methods is? Try this:
In [24]: mystr.endswith?
Type: builtin_function_or_method
Base Class: <type'builtin_function_or_method'>
String Form: <built-in method endswith of strobject at 0x445908>
Namespace: Interactive
Docstring:
S.endswith(suffix[, start[, end]]) ->bool
Return Trueif S ends with the specified suffix,False otherwise.
With optional start,test S beginning at that position.
With optional end, stop comparing S at that position.
suffix can also be a tuple of strings to try.
One question mark at the end of the line brings up a help dialog for the obejct, including the docstring for a function or method.
If the source code for the object is available, then two question marks will display the source.
And, one more helpful tip about entering code into iPython: since the interpretter formats your code for you automatically, e.g. by adding an indentation after a for or if statement, when you copy and paste code in, you'll have extra indentations where you don't want them. There are two ways around this: 1) you can turn off autoindentation by issuing the %autoindent command, or two, you can choose to paste your code into a block after issuing the %cpaste command. The latter will accept any arbitrarily large block of code with no formatting other than what is provided in the pasted text. When you are finished entering code into a %cpaste block, just enter two hyphens, a la " -- " and press return to execute the code. We'll use this in the next session.
Using the Python Debugger from iPython
ValueError? KeyError? NameError? If these sound familiar, the Python Debugger might be for you. PDB is a system that sits back quietly until an error is raised, either by the system, or by a try and except block that you've coded, at which point it springs into action, interrupting your program, and allowing you to peek inside the mangled mess you've woven. Sometimes this is valuable because you've written a complicated control loop and lost track of the ordering. From within PDB, you can check out the values of your variables and make sure they are what you wanted them to be. Sometimes the format of a file isn't what you expected, or a variable you expected to be available in your namespace isn't there after all. So let's look at the basic functions of the debugger, and then we'll check into using it in iPython.
%cpaste
#now just paste in the block below.importpdb#we gotta import the module to be able to use it# a simple loop to print each letter in a list of letters, then remove the letter from the list. What could possibly go wrong?
letters =['a','b','c','d','e','f','g']for let in letters:
print let
letters.remove(let)
--
a
c
e
g
Okay, so it appears we are skipping letters as we print them... Let's try setting a trace point in our loop so that we can poke around:
%cpaste
letters =['a','b','c','d','e','f','g']for let in letters:
pdb.set_trace()print let
letters.remove(let)
--
Now, when we run the code, the debugger will launch when we get to this pdb.set_trace() call.
><string>(4)<module>()(Pdb)
We can poke around here using commands like dir():
If we issue the always helpful "?" in the debugger, we get a useful help summary:
(Pdb) ?
Documented commands(typehelp<topic>):
========================================
EOF breakcommands debug h l pp s up
a bt condition disable helplist q step w
alias c cont down ignore n quit tbreak whatis
args cl continue enable j next r u where
b clear d exit jump p return unalias
Miscellaneous help topics:
==========================execpdb
Undocumented commands:
======================
retval rv
The chiefly useful commands to know here are continue, next, step, list, args, and the all-important exit (which gets us out of the debugger).
We can also inspect the values of variables by simply typing their name.
The whole idea of the debugger is to step into your code wherever you had a problem, and see what went awry. We jump in right where we entered the set_trace() call. If we take a look at our current iterated variable, we see that the first "let" is indeed "a":
(Pdb) let
Out[4]: 'a'
Things started off according to plan, so we need to advance in the code to see what will go wrong. For this, we have three options: step, next, and continue. step will onyl advance one line of code, whether that takes us into another function or not, whereas next will continue to the next line of our code. Meanwhile, continue will take us to the next break point, if there is one, or to the end of the code's execution if not. In this case, we want to use next to advance to the next line of our code, which should be printing the "let" variable, then removing it from the letters list.
(Pdb) n
a
# excellent, printed as expected(Pdb) n
><string>(4)<module>()(Pdb) letters
Out[4]: ['b','c','d','e','f','g']#and we see that the first 'a' has indeed been removed from the list.(Pdb) n
><string>(5)<module>()#Now we should be back at the top of the loop, so let's check this out:(Pdb) letters
Out[4]: ['b','c','d','e','f','g']#okay, letters hasn't done anything weird...(Pdb) let
Out[4]: 'c'# but, whoa! What happened to "b"?
So, we can see that as we restart the loop for the second pass through, our iterable variable becomes "c", not "b". This is the source of our confusion, and is due to the fact that Python uses your iterable as an index, not as a member of the list. On the second pass through the loop, Python is using the i+1th variable in the list, and that list has just been modified to be one letter shorter on the left-hand side, meaning that the i+1th position in the list is now "c" instead of "b" (now at position i).
In short, we can avoid this classic loop issue by adding things to a new list, then after the loop is finish, removing everything in the new list from the original one (and there other work-arounds for this structure).
Post-mortem Debugging
In [29]: %cpaste
def count_zombies(people):
zombie_counter =0for person in people:
if person.find('dead')!= -1:
zombie_counter +=1print"Looks like we've got about " + zombie_counter + " zombies in here!"
people =['dead_matt','rich_is_dead','Terry_lives!','Rose_from_the_dead']
count_zombies(people)
--
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
/Users/matt/<ipython console>in<module>()
/Library/Python/2.5/site-packages/ipython-0.8.3.svn.r3001-py2.5.egg/IPython/iplib.pycin ipmagic(self, arg_s)951else:
952 magic_args =self.var_expand(magic_args,1)
-->953return fn(magic_args)954955def ipalias(self,arg_s):
/Library/Python/2.5/site-packages/ipython-0.8.3.svn.r3001-py2.5.egg/IPython/Magic.pycin magic_cpaste(self, parameter_s)3157ifnot par:
3158 b =textwrap.dedent(block)
->3159exec b inself.user_ns3160self.user_ns['pasted_block']= b
3161else:
/Users/matt/<string>in<module>()
/Users/matt/<string>in count_zombies(people)TypeError: cannot concatenate 'str'and'int' objects
In [30]:
So, we've clearly made a TypeError, and in this case, our error message is fairly explicit: we've tried to concatenate the string "Looks like we've got about" with the integer stored in the variable zombie_count. But let's use this as a chance to unearth our error with the post-mortem debugger:
In [30]: pdb.pm()><string>(7)count_zombies()#We have arrived in the underworld of the count_zombies() function, where our last exception was thrown.#let's have a look around(Pdb)dir()
Out[30]: ['people','person','zombie_counter']#good, we see the variables we expect to see.(Pdb) people
Out[30]: ['dead_matt','rich_is_dead','Terry_lives!','Rose_from_the_dead'](Pdb) person
Out[30]: 'Rose_from_the_dead'(Pdb) zombie_counter
Out[30]: 3#And they all have the values we expect. Now, what say we play higher power for a moment?(Pdb)print"Looks like we've got about" + str(zombie_counter) + " zombies in here!"
Looks like we've got about 3 zombies in here!
The point being that 1) we can pick up the last exception that was thrown and poke around, and 2) we can change values in the debugger and execute code the way it should have been done the first time, i.e. giving us a chance to see if things had been written differently, if they would have produced the desired outcome.
Using the Debugger in Scripts and Programs
Most of the real work we do with Python is not done in an interpreter, so it can be helpful to have debugging statements called from scripts and programs. Here we'll illustrate how to call the debugger only when we're likely to need it: by using exceptions and if statements.
In this first example, we use the debugger calls inside of a control loop.
importsysimportpdbdef count_zombies(people):
zombie_counter =0for person in people:
if person.find('dead')!= -1:
zombie_counter +=1try:
print"Looks like we've got about " + zombie_counter + " zombies in here!"except:
pdb.set_trace()else:
print"All clear!"
people =['dead_matt','rich_is_dead','Terry_lives!','Rose_from_the_dead']
count_zombies(people)
If we use list, jump, and step, we can find the error in our code. But we can also put the debugger statements around our function calls, which is generally preferable.
importsysimportpdbdef count_zombies(people):
zombie_counter =0for person in people:
if person.find('dead')!= -1:
zombie_counter +=1print"Looks like we've got about " + zombie_counter + " zombies in here!"else:
print"All clear!"
people =['dead_matt','rich_is_dead','Terry_lives!','Rose_from_the_dead']try:
count_zombies(people)except:
pdb.set_trace()
When we debug this code, we'll find the line above the exception, and run it ourselves with corrected syntax. In order to do so, we need to use the
recursive debugger (the debug command from within the debugger), and then step through our code.
Finally, let's consider making our code a little more grown-up by giving the user an option to run the debugging code or not. This a trivial example
for our small program, but as the code grows, so do the possibilities of doing something unexpectedly wrong. Adding debug code as you go is time
consuming, but if you plan on a large program to be used by other people, can be worth doing.
importsysimportpdbdef check_debug(arg):
if(arg)and(arg !='-d'):
print"Zombie Script Usage: -d for DEBUG MODE"sys.exit()elif(arg)and(arg =='-d'):
print"Using DEBUG MODE"return1else:
return0def count_zombies(people):
zombie_counter =0for person in people:
if person.find('dead')!= -1:
zombie_counter +=1print"Looks like we've got about " + zombie_counter + " zombies in here!"
people =['dead_matt','rich_is_dead','Terry_lives!','Rose_from_the_dead']try:
debugflag = check_debug(sys.argv[1])exceptIndexError:
print"Running in NORMAL MODE"
debugflag =0if debugflag ==1:
try:
count_zombies(people)except:
pdb.set_trace()else:
count_zombies(people)
Exercises:
1) Use the debugger to find the error and fix this code. Describe precisely what the errors were and how you fixed them.
def count_teachers(teachers, teach_count):
rich_count = 0
for teach in teachers:
if teach == "Rich" and rich_count < 2:
rich_count += 1
teach_count += 1
teachers.remove(teach)
elif not teach in teachers:
teach_count += 1
teachers = ['Matt', 'Angela', 'Rich', 'Terry', 'Aaron', 'Rich']
teach_count = 0
count_teachers(teachers, teach_count)
print teach_count, "teachers teach this class."
2) From ipython, type %magic to read about more magic functions that we did not discuss. Usually from inside of iPython, if you type "ls" you will get the directory listing of the current working directory. Change this behavior so that it displays the long directory listing (i.e. like using ls -l in the terminal).
3) What is locals() used for? How could you use it instead of the dir() function in the debugger?
What to do when you don't know what to do: the interpreter, the debugger, and some useful resources
Topics:
Today You Will:
Learn to use Python Interpreters and the Python Debugger module to explore your programs and get out of jams.
The interpreter lets you interact with Python: as opposed to writing a script that it executed all at once, the interpreter allows you to step through the code piece by piece as you enter it. This can be a big help in both exploring how you should implement an algorithm in the first place and also in troubleshooting code that's just not doing what you thought it was going to do.
Running the default Python Interpreter
We'll start with the basic Python Interpreter, then we'll move on to a fancier-pants'd interpreter in a bit.
To launch the interpreter, simply call python with no script name after it:
$ python
The >>> is the prompt for you to enter commands at, so I'll use it from here on out if I mean for you to enter something into the interpreter (as opposed to the command line).
Let's start off by assigning a value to a variable.
And now if we type the name of the variables, "people", we will see the value assigned to it:
We can manipulate the variables and get the feedback instantly:
Note that the last command didn't change the variable, it just showed us the output of multiplying the variable by 3 (which, for a list, means to repeat its contents 3 times).
The interpreter has a couple of resident functions available to help us along in this interactive endeavor. The first you should know is help().
As the help function has just informed us, you can use help() to get help on specific modules, keywords, or topics. Let's use it to get the low-down on the other very useful interpreter function, dir().
This shows us that dir() can tell us about objects in our namespace, which in non-jargon means that dir() can tell us about things like variables and modules and functions, and which ones we have access to at whatever point we're at in our program.
So what does dir() do by itself?
So, we should recognize our list variable, "people", from before. So, what does dir() have to say about that?
This command shows us ALL the things that belong to our list variable, including the methods that we can call to manipulate it, such as .append(), etc. It also shows us a whole bunch of internal objects that belong to our list variable (which don't belong to us, and we don't get to use them -- at least not in this class).
So, hopefully you can see how to use this power for good: dir() can tell you all the methods and functions available to an object. For those of us that prefer to work in the interpreter, we find ourselves only rarely consulting exterior documentation. We can use dir() to find out what is available to an object, and then we can use help() to figure out what things are:
Which very succinctly tells us what we've already learned: people.append() will add whatever object we put in the parentheses to the end of the people list.
Using iPython from the SciPy Superpack
The default Python interpretter is a very basic interface. There are things that you'd like to be able to do, such as enter more than one line of code at a time, or perhaps look back through your history of commands. The enhanced iPython interpreter provides these perks and more.
You can start off by launching iPython from the command line:
$ ipython
iPython launches and tells us what version we're running, and gives us a couple of tips. Just typing a question mark and pressing enter will get us a comprehensive introduction to iPython, which I'll leave to you to peruse in your leisure time. You'll notice that the prompt for iPython includes a line number for each line of your code, signified on the first line by ln [1]:. The %quickref guide gives us a succint description of the fanciness of iPython. For starters, you have instant access to the system prompt for commands like "ls" and "cd."
A number of "magic" functions, all of which start with a percent sign "%", provide functionality that the regular interpretter cannot provide. One for regular use is %history, which will show you the lines of code you have previously entered in this session:
And if you look closely at that output, you might correctly suspect that you can also use %hist as a shortcut. This can be really helpful for repeating a block of code a couple of times, in which case, you might not want the line numbers getting in the way of your cutting-and-pasting the code back to the command line. Check out this simple example of a for loop printing our characters from a string, then viewing the previous commands:
Notice that all the "magic" or "system" commands like %history and "ls" register with an _ip.magic or _ip.system function prefix around them. But the lines of actual python code appear in order, and formatted, such that you could easily copy them back to the prompt again, or to a text editor. Or, alternatively, you can use the %save command to capture the desired subset of your history and send to a textfile:
This saves lines 8 and 9 of the history (our little for loop) into a separate file. Now, if we want to change something up, we can edit the file with the %edit command, and when we exit with our editor, the file will run again.
Okay, so there are lots of nice little magic tricks in iPython, and you can use teh %quickref guide to explore them more on your own. Meanwhile, lemme show you a couple of other handy tricks that don't involve magic functions.
Remember in the basic Python interpretter, we could use dir() to find out what objects belonged to a given object? Well, in iPython, all we have to do to capture the same information is type the object and period, then press the tab key twice.
This shows us all the things we can use or modify that belong to our variable "mystr", from our for loop.
And what if you don't know what one of those functons or methods is? Try this:
One question mark at the end of the line brings up a help dialog for the obejct, including the docstring for a function or method.
If the source code for the object is available, then two question marks will display the source.
And, one more helpful tip about entering code into iPython: since the interpretter formats your code for you automatically, e.g. by adding an indentation after a for or if statement, when you copy and paste code in, you'll have extra indentations where you don't want them. There are two ways around this: 1) you can turn off autoindentation by issuing the %autoindent command, or two, you can choose to paste your code into a block after issuing the %cpaste command. The latter will accept any arbitrarily large block of code with no formatting other than what is provided in the pasted text. When you are finished entering code into a %cpaste block, just enter two hyphens, a la " -- " and press return to execute the code. We'll use this in the next session.
Using the Python Debugger from iPython
ValueError? KeyError? NameError? If these sound familiar, the Python Debugger might be for you. PDB is a system that sits back quietly until an error is raised, either by the system, or by a try and except block that you've coded, at which point it springs into action, interrupting your program, and allowing you to peek inside the mangled mess you've woven. Sometimes this is valuable because you've written a complicated control loop and lost track of the ordering. From within PDB, you can check out the values of your variables and make sure they are what you wanted them to be. Sometimes the format of a file isn't what you expected, or a variable you expected to be available in your namespace isn't there after all. So let's look at the basic functions of the debugger, and then we'll check into using it in iPython.
a
c
e
g
Okay, so it appears we are skipping letters as we print them... Let's try setting a trace point in our loop so that we can poke around:
Now, when we run the code, the debugger will launch when we get to this pdb.set_trace() call.
We can poke around here using commands like dir():
If we issue the always helpful "?" in the debugger, we get a useful help summary:
The chiefly useful commands to know here are continue, next, step, list, args, and the all-important exit (which gets us out of the debugger).
We can also inspect the values of variables by simply typing their name.
The whole idea of the debugger is to step into your code wherever you had a problem, and see what went awry. We jump in right where we entered the set_trace() call. If we take a look at our current iterated variable, we see that the first "let" is indeed "a":
Things started off according to plan, so we need to advance in the code to see what will go wrong. For this, we have three options: step, next, and continue. step will onyl advance one line of code, whether that takes us into another function or not, whereas next will continue to the next line of our code. Meanwhile, continue will take us to the next break point, if there is one, or to the end of the code's execution if not. In this case, we want to use next to advance to the next line of our code, which should be printing the "let" variable, then removing it from the letters list.
So, we can see that as we restart the loop for the second pass through, our iterable variable becomes "c", not "b". This is the source of our confusion, and is due to the fact that Python uses your iterable as an index, not as a member of the list. On the second pass through the loop, Python is using the i+1th variable in the list, and that list has just been modified to be one letter shorter on the left-hand side, meaning that the i+1th position in the list is now "c" instead of "b" (now at position i).
In short, we can avoid this classic loop issue by adding things to a new list, then after the loop is finish, removing everything in the new list from the original one (and there other work-arounds for this structure).
Post-mortem Debugging
So, we've clearly made a TypeError, and in this case, our error message is fairly explicit: we've tried to concatenate the string "Looks like we've got about" with the integer stored in the variable zombie_count. But let's use this as a chance to unearth our error with the post-mortem debugger:
The point being that 1) we can pick up the last exception that was thrown and poke around, and 2) we can change values in the debugger and execute code the way it should have been done the first time, i.e. giving us a chance to see if things had been written differently, if they would have produced the desired outcome.
Using the Debugger in Scripts and Programs
Most of the real work we do with Python is not done in an interpreter, so it can be helpful to have debugging statements called from scripts and programs. Here we'll illustrate how to call the debugger only when we're likely to need it: by using exceptions and if statements.
In this first example, we use the debugger calls inside of a control loop.
If we use list, jump, and step, we can find the error in our code. But we can also put the debugger statements around our function calls, which is generally preferable.
When we debug this code, we'll find the line above the exception, and run it ourselves with corrected syntax. In order to do so, we need to use the
recursive debugger (the debug command from within the debugger), and then step through our code.
Finally, let's consider making our code a little more grown-up by giving the user an option to run the debugging code or not. This a trivial example
for our small program, but as the code grows, so do the possibilities of doing something unexpectedly wrong. Adding debug code as you go is time
consuming, but if you plan on a large program to be used by other people, can be worth doing.
Exercises:
1) Use the debugger to find the error and fix this code. Describe precisely what the errors were and how you fixed them.
def count_teachers(teachers, teach_count): rich_count = 0 for teach in teachers: if teach == "Rich" and rich_count < 2: rich_count += 1 teach_count += 1 teachers.remove(teach) elif not teach in teachers: teach_count += 1 teachers = ['Matt', 'Angela', 'Rich', 'Terry', 'Aaron', 'Rich'] teach_count = 0 count_teachers(teachers, teach_count) print teach_count, "teachers teach this class."2) From ipython, type %magic to read about more magic functions that we did not discuss. Usually from inside of iPython, if you type "ls" you will get the directory listing of the current working directory. Change this behavior so that it displays the long directory listing (i.e. like using ls -l in the terminal).
3) What is locals() used for? How could you use it instead of the dir() function in the debugger?