What to Do When You Don't Know What to Do: the interpreter, the debugger, and some useful resources

Written by Matt Davis, updated 7/20/11 by Mike Lawson

Topics:

  • Python interpreter, help() and dir()
  • iPython interpreter (and SciPy)
  • 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

Introduction:

In addition to understanding the program and script files that you've written, Python can also interpret things one line at a time. When we type python at the command line, we're invoking the python interpreter, whose job it is to "interpret" our python code and translate it for the operating system on our computer, which in turn further translates things for the CPU that will actually make the calculations.

So the interpreter also lets you interact with Python: as opposed to writing a script that is interpreted and executed all at once, the interpreter can let you 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
Enthought Python Distribution -- www.enthought.com
Version: 7.0-1 (64-bit)
 
Python 2.7.1 |EPD 7.0-1 (64-bit)| (r271:86832, Dec  3 2010, 15:56:20)
[GCC 4.0.1 (Apple Inc. build 5488)] 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 intend 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 = ['Mike', 'Peter', 'Aaron']
And now if we type the name of the list we just defined, people, we will see the value assigned to it:
>>> people
['Mike', 'Peter', 'Aaron']
We can manipulate the variables and get the feedback instantly:
>>> people * 3
['Mike', 'Peter', 'Aaron', 'Mike', 'Peter', 'Aaron', 'Mike', 'Peter', 'Aaron']
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.7!  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://docs.python.org/tutorial/.
 
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 and
return 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 dir in module __builtin__:
 
dir(...)
    dir([object]) -> list of strings
 
    If called without an argument, return the names in the current scope.
    Else, return an alphabetized list of names comprising (some of) the attributes
    of the given object, and of attributes reachable from it.
    If the object supplies a method named __dir__, it will be used; otherwise
    the default dir() logic is used and returns:
      for a module object: the module's attributes.
      for a class object:  its attributes, and recursively the attributes
        of its bases.
      for any other object: 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.

So what does dir() do by itself?

>>> dir()
['__builtins__', '__doc__', '__name__', '__package__', 'people']

So, we should recognize our list variable, "people", from before. So, what does dir() have to say about that?

>>> dir(people)
['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__delslice__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getslice__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__setslice__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']

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


The default Python interpreter 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

Enthought Python Distribution -- http://www.enthought.com

Python 2.7.1 |EPD 7.0-1 (64-bit)| (r271:86832, Dec 3 2010, 15:56:20)
Type "copyright", "credits" or "license" for more information.

IPython 0.10.1 -- 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**.
 
 
[[code format="python"]]
In [3]: ls
data/        programFiles/
 


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:

In [4]: %history
1: _ip.system("ls -F ")
2: _ip.magic("cd S6.2/")
3: _ip.magic("hist ")
4: _ip.magic("history ")

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.py 8-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
 

Let's take a second to try and understand how these "magic" functions actually work. We'll use another useful function, %macro, as an example.
In [18]: macro?
Define a set of input lines as a macro for future re-execution.
 
Usage:
%macro [options] name n1-n2 n3-n4 ... n5 .. n6 ...
 
Options:
 
-r: use 'raw' input.  By default, the 'processed' history is used,
so that magics are loaded in their transformed version to valid
Python.  If this option is given, the raw input as typed as the
command line is used instead.
 
This will define a global variable called `name` which is a string
made of joining the slices and lines you specify (n1,n2,... numbers
above) from your input history into a single string. This variable
acts like an automatic function which re-executes those lines as if
you had typed them. You just type 'name' at the prompt and the code
executes.
 
The notation for indicating number ranges is: n1-n2 means 'use line
numbers n1,...n2' (the endpoint is included).  That is, '5-7' means
using the lines numbered 5,6 and 7.
 
Note: as a 'hidden' feature, you can also use traditional python slice
notation, where N:M means numbers N through M-1.
 
For example, if your history contains (%hist prints it):
 
44: x=1
45: y=3
46: z=x+y
47: print x
48: a=5
49: print 'x',x,'y',y
 
you can create a macro with lines 44 through 47 (included) and line 49
called my_macro with:
 
In [55]: %macro my_macro 44-47 49
 
Now, typing `my_macro` (without quotes) will re-execute all this code
in one pass.
 
You don't need to give the line-numbers in order, and any given line
number can appear multiple times. You can assemble macros with any
lines from your input history in any order.
 
The macro is a simple object which holds its value in an attribute,
but IPython's display system checks for macros and executes them as
code instead of printing them when you type their name.
 
You can view a macro's contents by explicitly printing it with:
 
'print macro_name'.
 
For one-off cases which DON'T contain magic function calls in them you
can obtain similar results by explicitly executing slices from your
input history with:
 
In [60]: exec In[44:48]+In[49]
 
 

Obviously this is a useful tool that could save us a lot of keystrokes. But where does this "magic" come from?
Navigate to the directory where you originally opened iPython, then cd into the folder titled "IPython." Do you see the file "Magic.py"? All of the "magic" scripts are contained within this file, and are written in python. iPython itself is just one massive collection of python scripts! (cool, huh?) Quit out of IPython for a second and open the "Magic.py" file in your favorite text editor, and search for "%macro" - does this look familiar?

Now let's talk about 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 (this is similar to the role of tab in the shell).
In [21]: mystr.
mystr.__add__           mystr.__mod__           mystr.expandtabs        mystr.rindex
mystr.__class__         mystr.__mul__           mystr.find              mystr.rjust
mystr.__contains__      mystr.__ne__            mystr.index             mystr.rpartition
mystr.__delattr__       mystr.__new__           mystr.isalnum           mystr.rsplit
mystr.__doc__           mystr.__reduce__        mystr.isalpha           mystr.rstrip
mystr.__eq__            mystr.__reduce_ex__     mystr.isdigit           mystr.split
mystr.__ge__            mystr.__repr__          mystr.islower           mystr.splitlines
mystr.__getattribute__  mystr.__rmod__          mystr.isspace           mystr.startswith
mystr.__getitem__       mystr.__rmul__          mystr.istitle           mystr.strip
mystr.__getnewargs__    mystr.__setattr__       mystr.isupper           mystr.swapcase
mystr.__getslice__      mystr.__str__           mystr.join              mystr.title
mystr.__gt__            mystr.capitalize        mystr.ljust             mystr.translate
mystr.__hash__          mystr.center            mystr.lower             mystr.upper
mystr.__init__          mystr.count             mystr.lstrip            mystr.zfill
mystr.__le__            mystr.decode            mystr.partition
mystr.__len__           mystr.encode            mystr.replace
mystr.__lt__            mystr.endswith          mystr.rfind
 

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 str object at 0x445908>
Namespace:    Interactive
Docstring:
S.endswith(suffix[, start[, end]]) -> bool
 
Return True if 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 object, 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 in 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 tangled web 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.
 
import pdb
#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():

(Pdb) dir()
 
Out[3]:
['In',
'Out',
'_',
'__',
'__IP',
'___',
'__builtins__',
'__name__',
'_dh',
'_i',
'_i1',
'_i3',
'_ih',
'_ii',
'_iii',
'_ip',
'_oh',
'_sh',
'help',
'let',
'letters',
'pasted_block',
'pdb']
 

If we issue the always helpful ? in the debugger, we get a useful help summary:

(Pdb) ?
 
Documented commands (type help <topic>):
========================================
EOF    break  commands   debug    h       l     pp      s        up
a      bt     condition  disable  help    list  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:
==========================
exec  pdb
 
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 only 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). Note in particular that Python's documentation says that "It is not safe to modify the sequence being iterated over in the loop" and that if you need to modify the object you're looping over, "you must iterate over a copy".

Post-mortem Debugging


In [28]: %cpaste
def count_zombies(people):
zombie_counter = 0
for person in people:
if person.find('dead') != -1:
zombie_counter += 1
 
print "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/lawson/<ipython console> in <module>()
 
/Library/Python/2.5/site-packages/ipython-0.8.3.svn.r3001-py2.5.egg/IPython/iplib.pyc in ipmagic(self, arg_s)
951         else:
952             magic_args = self.var_expand(magic_args,1)
--> 953             return fn(magic_args)
954
955     def ipalias(self,arg_s):
 
/Library/Python/2.5/site-packages/ipython-0.8.3.svn.r3001-py2.5.egg/IPython/Magic.pyc in magic_cpaste(self, parameter_s)
3157         if not par:
3158             b = textwrap.dedent(block)
-> 3159             exec b in self.user_ns
3160             self.user_ns['pasted_block'] = b
3161         else:
 
/Users/matt/<string> in <module>()
 
/Users/matt/<string> in count_zombies(people)
 
TypeError: cannot concatenate 'str' and 'int' objects
 
In [29]:
 

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 call the debugger from inside of a control loop.

import sys
import pdb
 
def count_zombies(people):
zombie_counter = 0
for person in people:
if person.find('dead') != -1:
zombie_counter += 1
try:
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.

import sys
import pdb
 
def count_zombies(people):
zombie_counter = 0
for person in people:
if person.find('dead') != -1:
zombie_counter += 1
print "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.

import sys
import pdb
 
def 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"
return 1
else:
return 0
 
def count_zombies(people):
zombie_counter = 0
for person in people:
if person.find('dead') != -1:
zombie_counter += 1
print "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])
except IndexError:
print "Running in NORMAL MODE"
debugflag = 0
 
 
if debugflag == 1:
try:
count_zombies(people)
except:
pdb.set_trace()
else:
count_zombies(people)
 



Exercises:


1) The code below is suppose to let us know when we've become edified enough to go outside and play. I'm having a hard time, however, because it doesn't wanna let me leave! Use the debugger to find the error and fix this code. Describe precisely what the errors were and how you fixed them.

#!/usr/bin/env python
 
import pdb
 
def get_things_done(todolist):
'''Function accepts a list of things left to learn, and prints an
integer value of how many thing we got done.'''
 
 
for items in todolist:
todolist.remove(item)
items_done_got_done_with += 1
print "We done got done with this many things:", items_done_got_done_with
 
 
 
 
def go_outside_and_play(things_left_to_learn):
'''Checks for permission to go outside and play.  All we have to do is finish our todo list.'''
 
if len(todolist) = 0:
print 'Woo!  Playtime!'
else:
print 'Oh, my, my, no... it's back to work with us!'
 
 
 
 
things_left_to_learn = ['computer science', 'complex analysis', 'molecular biology', 'population genetics', 'complete writings of Bertrand Russell', 'patience', 'compassion', 'understanding', 'gaming the stock market', 'humility', 'generalized stochastic statistical models', 'digital signal processing', 'how to hit a softball like a softball instead of like a baseball']
 
 
get_things_done(things_left_to_learn)
go_outside_and_play(things_left_to_learn)
 
 
 


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?

EXTRA CREDIT!!! 4) One of the added perks to using iPython over the default Python Interpreter is the ability to shamelessly input unix commands. Let's pretend for a second that iPython does not exist, but we are confident it's possible to write a program that can parse out and run unix commands from (almost) any input. As a proof of principle, write a Python script that accomplishes three things:

a) Takes input from the user in the same way as iPython.
b) Identifies any of the following unix commands in the input. Feel free to copy/paste the following tuple into your code (hint: use .startswith(tuple1) to iterate through the tuple)

tuple1 = "ls", "pwd", "who", "whoami"

c) Allows the user to stop entering input in the same way as iPython (also known as everything that happens once you type "quit").

Solutions


1)
#!/usr/bin/env python
 
import pdb
 
def get_things_done(todolist):
'''Function accepts a list of things left to learn, and prints an
integer value of how many thing we got done.'''
items_done_got_done_with = 0
todolistCopy = todolist[:]
print todolistCopy, 'is a list'
for item in todolist:
print item
todolistCopy.remove(item)
print todolistCopy
items_done_got_done_with += 1
print "We done got done with this many things:", items_done_got_done_with
print 'We still got %s things left to do.' % (len(todolistCopy))
return todolistCopy
 
def go_outside_and_play(things_left_to_learn):
'''Checks for permission to go outside and play.  All we have to do is finish our todo list.'''
if len(todolist) == 0:
print 'Woo!  Playtime!'
else:
print 'Oh, my, my, no... it\'s back to work with us!'
 
things_left_to_learn = ['computer science', 'complex analysis', 'molecular biology', 'population genetics', 'complete writings of Bertrand Russell', 'patience', 'compassion', 'understanding', 'gaming the stock market', 'humility', 'generalized stochastic statistical models', 'digital signal processing', 'how to hit a softball like a softball instead of like a baseball']
 
todolist = get_things_done(things_left_to_learn)
go_outside_and_play(things_left_to_learn)
 
"""
 
(1) invalid syntax in line 13, need == instead of =
(2) invalid syntax in line 16, need to escape the quote
(3) global name 'item' not defined in line 5, should change 'items' in
line 4 to 'item'
(4) local variable 'items_done_got_done_with' referenced in line 7 before
assignment, need to define the variable and set it equal to 0 before for loop
(5) global name 'todolist' not defined in line 12, need to return todolist
in line 9 and retrieve it from the function in line 20
(6) Finally, there are no more exceptions thrown, but the result isn't what
we expect.  We never run out of things to do.  Should add pdb.set_trace() to
the for loop.  As I 'next' through the list checking the variables 'item' and
'todolist', I notice that 'item' is skipping every other item in the 'todolist.'
This is clearly because it is iterating by index and not by 'item' name -- as
I remove an item, the list gets shorter and the next item in the list gets
skipped.  The problem is that same as for the list of abc's in the lecture.
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).
 
"""

2)
In[1]: %alias ls ls -l
3)
locals() gives you a dictionary of both variable names (as the keys) and their values (as the values of the keys).
4)
import os
linecount = 1
tuple1 = "ls", "pwd", "who", "whoami"
 
while(True):
    line = raw_input("In [" + str(linecount) + "]: ")
    if line.startswith(tuple1):
        os.system(line)
    if(line == "quit"):
        break
    linecount = linecount + 1