UrbanSim Capstone - Notes on Python and IDEs

These are some notes on using Python and the Eclipse Interactive Development Enviroment. They are fairly terse, and are intended to accompany a demonstration. For more detail, see the Python Tutorial and the help pages for Eclipse.

Versions

Python continues to evolve -- the current stable release is 2.5.1. Download from the Python website.

Python Characteristics

Running Python

This section gives directions for using Python for experimentation at the keyboard, and simple uses. For more sophisticated use, Eclipse (or Wing) is better.

The simplest way of starting Python is by typing "python" at a command shell on Windows, Linux, or Macintosh. However, many people prefer to use a simple interactive environment, e.g. IDLE.

Once you've started Python, you get a command prompt and can enter expressions to be evaluated, assignment statements, definitions, etc. In Python new variables spring into existence the first time they are used -- you don't need to declare them first. As in most other dynamically typed languages, variables can hold values of different types at different times.

You can also ask Python to read in a file and evaluate its contents using a command like "python myfile.py".

Language Constructs

An interesting aspect of Python syntax is that whitespace is significant, and is used to group statements.  As a result you don't need lots of { } like in Java.  

Comments start with a # -- everything on the line after the # is ignored

Some important data types:
integers, floats, booleans (True, False), lists, strings, dictionaries
Expressions - examples:
3
3+4
x*(y+sqrt(2))
"eggs" + "spam"
u"this is a unicode string"
'you can also use single quotes for a string'
["this", "is", "a", "list"]
x[3]
x[4:5]
{'a': 42, 'b': 100}
Assignment statements
x = y+3
Conditional statements
if x>3:
    print "and now for something completely different"

if x<0:
    print "negative"
elif x==0:
    print "zero"
else:
    print "positive"
Loops
s = ["fie", "fie", "fo", "fum"]
for x in s:
    print x
          for i in range(10):
    print i
Function definitions
def double(n):
    return 2*n

Importing Functions and Classes

Suppose you've set up your PYTHONPATH to include the directory 'c:\workspace' (on windows) or '/Users/yourname/workspace' (Macintosh). Suppose also that workspace has a subdirectory named tutorial.  This directory should contain an empty file __init__.py (if you forget this, Python won't figure out how to import correctly from this directory.  Ugh!) It also contains a file 'spammaker.py' with the following contents:

def make_spam(n):
    """return a string consisting of n copies of 'spam', 
    separated by commas"""
    ans = ""
    for i in range(n-1):
        ans = ans + "spam, "
        # if n is greater than 0, add the final 'spam'
        if n>0:
            ans = ans + "spam"
    return ans

Now at the keyboard start Python and type:

from tutorial.spammaker import make_spam
make_spam(10)

Objects and Classes

Class definitions are reasonably intuitive. Here's a definition of a Point class (also in the tutorial.zip file):

from math import sqrt
import unittest

class Point(object):
    """Point class with public x and y attributes """
 
    def __init__(self, x=0, y=0):
        self.x = x
        self.y = y
 
    def dist(self, p):
        """return the Euclidian distance between self and p"""
        dx = self.x - p.x
        dy = self.y - p.y
        return sqrt(dx*dx + dy*dy)
 
    def reset(self):
        self.x = 0
        self.y = 0

    def __add__(self, p):
        """return a new point found by adding self and p. This method is
        called by e.g. p+q for points p and q"""
        return Point(self.x+p.x, self.y+p.y)
 
    def __repr__(self):
        """return a string representation of this point. This method is
        called by the repr() function, and
        also the str() function. It should produce a string that, when
        evaluated, returns a point with the 
        same data."""
        return 'Point(%d,%d)' % (self.x, self.y)
 
class TestPoint(unittest.TestCase):
 
    def test_dist(self):
        p1 = Point(1,2)
        p2 = Point(5,-1)
        self.assertEqual(p1.dist(p2), 5.0)
 
    def test_add(self):
        p1 = Point(1,2)
        p2 = Point(5,-10)
        sum = p1+p2
        self.assertEqual(sum.x, 6)
        self.assertEqual(sum.y, -8)

    def test_repr(self):
        p1 = Point(1,2)
        self.assertEqual(str(p1), 'Point(1,2)')
 
if __name__ == '__main__':
    unittest.main()
This includes a unit test. Good practice is to do test-first development.

A bug that will probably bite you sometime:

p = Point(10,20)
p.reset

We could define a variant of this class that hides the x and y attributes and only accesses them via methods:

from math import sqrt
import unittest

class Point(object):
    """Point class with hidden x and y attributes (these must be accessed
    using a method)"""

    def __init__(self, x=0, y=0):
        self._x = x
        self._y = y
 
    # access methods
    def get_x(self):
        return self._x
 
    def get_y(self):
        return self._y
 
    def set_x(self, x):
        self._x = x
 
    def set_y(self, y):
        self._y = y
 
    def dist(self, p):
        dx = self.get_x() - p.get_x()
        dy = self.get_y() - p.get_y()
        return sqrt(dx*dx + dy*dy)
 
class TestPoint(unittest.TestCase):
 
    def test_dist(self):
        p1 = Point(1,2)
        p2 = Point(5,-1)
        self.assertEqual(p1.dist(p2), 5.0)
 
if __name__ == '__main__':
    unittest.main()

Multiple Inheritance

Python supports multiple inheritance (although I find that I almost never use it).  In case you care, recent versions of Python use the C3 Method Resolution Order. But if you care whether this is different from depth first and then left to right search order, you are writing code that is way too complicated (or maybe taking the grad programming languages class).

Programming in a Functional Style

If you like using lambdas, list comprehensions, etc (as used extensively in languages such as ML, Miranda, or Haskell), Python provides them as well.

from math import sqrt
x = range(10)
map(sqrt, x)
map(lambda n: 2*n, x)
stuff = ['fie', 'fie', 'fo', 'fum']
filter(lambda s: len(s)>2, stuff)
zip(range(1,5), range(31,35), ['a', 'b', 'c', 'd'])

Python Documentation

Documentation is available at http://www.python.org/doc/ for both the current and older versions.

You can browse it online, but if you're going to use it very often, download a zip file of the html version and browse locally.

The tutorial is short and quite good. The "Library Reference" is indispensible. I'd suggest looking through the table of contents, and reading about the the basics (e.g. strings, dictionaries, etc.), to get an idea what's there. The "Language Reference" is accurate but painful (lots of BNF, no examples).

Installing Packages

The packages in the "Library Reference" come with your Python copy.  For other packages, find them in the Python Cheese Shop, on sourceforge or wherever, download, and install them.  These other packages end up in a directory 'site-packages'.  Python has a search path that includes this directory when doing imports.  For example, on my Windows machine this is at C:\Python25\Lib\site-packages; on Mac at /Library/Frameworks/Python.framework/Versions/Current/lib/python2.5/site-packages

One option is using Easy Install.  See the PEAK webpage for more info.  Download ez_setup.py to your computer. Run it (from the directory it's in) by typing

 python ez_setup.py

Then you can install packages using one of these commands:

easy_install [filename]
easy_install [URL]
easy_install [package name]
For the last version, easy_install will go look in the Cheese Shop for the package. If you're in luck, you'll get what you want.

If you want to track down the exact version yourself, or if easy_install doesn't do what you want, for Windows, ideally you'll find an exe file on sourceforge -- just run it to install into site-packages.

For Mac, ideally you'll find a dmg file (check http://pythonmac.org/packages/ and sourceforge, or google it).  Sometimes you'll need to build from source though (usually this is not a problem on Mac).  See http://trondheim.cs.washington.edu/cgi-bin/trac.cgi/wiki/MacintoshLore for some hints on this.

Eclipse

On the undergrad lab machines, Eclipse and PyDev are already installed. However, you need to configure PyDev to tell it where the Python interpreter is (it's in c:python25\python.exe). Once this is done, switch to the PyDev perspective, and create a new project called tutorial. Define a test script in a file called (say) test.py. Run it.

After that's working, get the tutorial.zip zip file and unzip it onto your desktop. Then use the Eclipse import command to import the files into tutorial. (Just get the files -- don't have another tutorial directory inside the first tutorial directory.) Now you should be able to run the unit tests on the Point class, or write a test script that uses spammaker.

On a personal machine, you'll need to first download Eclipse from http://www.eclipse.org/ and set it up. Then get PyDev -- to do this, in Eclipse, go to the update manager (inside the help menu) and add the update site: http://pydev.sourceforge.net/updates/ (eclipse should do the rest). Then carry on as with the lab machine directions.

Things to watch for in the Eclipse demo: