UrbanSim Capstone - Notes on Python and IDEs

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

Versions

Python continues to evolve. The Enthought edition of Python uses Python 2.4.3, and the tutorial linked above is for that version. You should be able to use 2.4.4 safely as well; we haven't converted UrbanSim to the current version, which is 2.5.

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. The Parrington Lab has SciTE installed.  The Enthought Python edition also comes with 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".

Quick List of Some 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.  UrbanSim standard: indent 4 spaces for each new level. Only use spaces, never tabs.

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))
"squid" + "clam"
["this", "is", "a", "list"]
x[3]
x[4:5]
{'a': 42, 'b': 100}
Assignment statements
x = y+3
Conditional statements
if x>3:
    print "big"
else:
    print "small"
Loops
s = ["fie", "fie", "fo", "fum"]
for x in s:
    print x
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).  Let's define a subdirectory of workspace called 'capstone'.  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 capstone.spammaker import make_spam
make_spam(10)

NumPy

(Please see the NumPy tutorial.)

Objects and Classes

Class definitions are reasonably intuitive. Here's a definition of a Point class:

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 __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.

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()

Eclipse and Wing

Things to watch for in the Eclipse demo:

Things to watch for in the Wing demo: