Lecture 4: Python, Maintaining State ------------------------------------ So last time we learned Perl on the first leg of our tour on scripting languages. Now we're going to talk about Python, which you may or may not like better than Perl. What about Python, then? - Python doesn't have the plethora of shortcuts and weird syntax that Perl does. Therefore, it tends to look a little "cleaner." However, your code may be longer and slower. - Python, like Perl, has about a billion extension modules. - You can interact with Python on the command line, using it to debug programs. - Like Perl, Python runs on Unix, Mac, and Windows. - Python is object-oriented. Python documentation: http://www.python.org/ is the online reference. The best Python book is undoubtedly "Python Essential Reference" by David Beazley (who also happens to be my advisor). Second edition goes to press next month. On our system, you'll start Python scripts with this: #!/usr/local/bin/python But first, let's just start it in interactive mode: $ python Python 2.0 (#5, Dec 5 2000, 10:10:43) [GCC 2.95.1 19990816 (release)] on linux2 Type "copyright", "credits" or "license" for more information. >>> In these notes, any line that starts with >>> means that >>> is the prompt, and I'm typing stuff in after the prompt, like this: >>> print "hi there" hi there That's a function call, but you can also evaluate expressions: >>> 5 5 >>> 5+8 13 >>> "hi there" 'hi there' Okay, so let's write Hello world: #!/usr/local/bin/python print "hi there" Well, that sure was exciting. You can import a Python program at the program with the execfile() function: >>> execfile("blah.py") Variables: variables are defined with an identifier which can be almost any string that doesn't start with a number or have a . in it. You don't prefix variables with $, @, or any kind of crazy thing like in Perl. So let's set the variable i to 5 here: >>> i = 5 You can find out a variable's value by evaluating it at the >>> prompt. >>> i 5 Now, let's talk about program blocks real quick. In most languages, you've seen so far, the curly braces { } denote program blocks. Well, in Python, things are just a little different. #!/usr/local/bin/python i = 9 if i < 10: print "hi there" print "Hey, I said.. HI THERE" else: print "changed my mind" Notice how there are no braces? Yet when you run the program, you get the output of the first two print statements. How does Python do blocks then? It's entirely by indentation. This program gives a syntax error: #!/usr/local/bin/python i = 9 if i < 10: print "hi there" print "Hey, I said.. HI THERE" else: print "changed my mind" That's because the second print statement is not indented, indicating that the "if" control block above has terminated. Thus, when that "else:" comes along, it doesn't have a matching "if". You also need the ":" after the test and the "else". Types: We've already seen the number and string types. There's a list type which you ought to know. You specify list literals with the square brackets [ ] >>> lst = ["so", "hi", "there", "again"] >>> lst ['so', 'hi', 'there', 'again'] To get at an individual element, use the square brackets again. Like Perl (and almost every other language), the index start at 0: >>> lst[3] 'again' >>> lst[0] 'so' Now, a list isn't just a random data type. It's an object, and each object has a whole bunch of methods associated with it. For example, append is a method that appends an element to a list: >>> lst.append("blah") >>> lst ['so', 'hi', 'there', 'again', 'blah'] But Python isn't too crazy with objects and methods. Getting the number of elements in a list has nothing to do with objects: >>> len(lst) 5 Like Perl, Python has about a million list manipulation operators. Get the book to see 'em all. A tuple is like a fixed-length list, except that once you create a tuple, you can't change it: >>> t = (3, 4, 5) >>> t[1] 4 >>> t[1] = 10 Traceback (most recent call last): File "", line 1, in ? TypeError: object doesn't support item assignment Those are the basic types. Python is fairly strict about them. For example, if you try to add a string and a number: "5" + 8 you get a type error. If you want to add these, you have to force the string to an integer: int("5") + 8 To turn an integer into a string, use the str() function. However strict it may be with types, Python plays it fast and loose with operators (which is pretty much the opposite of Perl). The result of an operation depends on the types you give to it- this is also called operator overloading. So + means three different things depending on the types: 5 + 8 => 13 (integer addition) "hi" + "there" => 'hithere' (string concatenation) ["a", "b"] + ["c", "d"] => ['a', 'b', 'c', 'd'] (list concatenation) So let's look at some flow control constructs, starting with the familiar if statement: if i == 5: print "how about that, i is 5" You can chain if statements together with elif: if movie == "Notting Hill": print "Wow, that's a bad one" elif movie == "The Spy Who Shagged Me": print "Yeah, baby, yeah!" else: print "I don't know that one." We've got the while loop, too: #!/usr/local/bin/python i = 0 while i < 10: print i i = i + 1 There's another loop, the for loop. This doesn't work like the C for loop- it works on lists. #!/usr/local/bin/python lst = ["this", "is", "my", "list"] for i in lst: print i You may be wondering, then, how you get the loop index to act like a real for loop? #!/usr/local/bin/python for i in range(0,10): print i (range() returns a list from the first argument up to but not including the second. If you give it an optional third argument (like range(0,10,2)) it steps by the third. Type it in to see.) Dictionaries: Python's version of the associative array is called a dictionary. Just like in Perl, this is quite a versatile data type. #!/usr/local/bin/python d = { "bad" : "Bruce Willis", "worse" : "Denise Richards", "AAIGGH" : "Arnold Schwarzenegger" } print d["bad"] In this example, d is the dictionary. You can add stuff to the dictionary like this: d["Julie thinks he sucks"] = "Matt Damon" To get at all of the keys, you have the use the dictionary's keys() method. Combine it with the for loop, and you can go through everything in the dictionary: for invective in d.keys(): print "'%s' is best described by %s" % (invective, d[invective]) Notice the output formatting there. Functions: Are really easy to define: def multiply(a, b): return a*b You call them like any other function: multiply(4,5) Classes/Objects in Python: By now, you've noticed that some features, like the keys of a dictionary, are only available as methods. If you're unfamiliar with objects, just think of it this way: - A "class" is like a data structure. - An "object" is an instance of a particular class. - An "attribute" is part of the data in an object. - A "method" is just a function that you can only apply to a particular kind of object. - There is absolutely nothing profound about any of this. If you accidentally call a method a function, no one's going to hunt you down and slap you around. Anyway, here's how to make a class in Python: class locomotive: def __init__(self, name): # constructor method self.name = name # this is an attribute self.speed = 0 # another attribute def faster(self): # a method self.speed += 1 def slower(self): # another method if self.speed == 0: print "We're not moving, you bozo." else: self.speed -= 1 To make an instance of an object, and do stuff with it, just do this: l = locomotive("K4 Pacific") print l.speed l.faster() Whee. At the python prompt, the dir() function is handy for finding all of the attributes of an object, though it doesn't seem to show methods: >>> dir(l) The dir() function is especially handy for finding attributes and methods of built-in datatypes: >>> s = "hi there" >>> dir(s) ['capitalize', 'center', 'count', 'encode', 'endswith', 'expandtabs', 'find', 'index', 'isalnum', 'isalpha', 'isdigit', 'islower', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'replace', 'rfind', 'rindex', 'rjust', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper'] Just try some of them out on the command line, like >>> s.split() You may have notice the replace method above. This is a simple search-and-replace feature, but it doesn't involve the more power regular expressions that we saw in the last lecture. To get those, you need to import the "re" module with the import statement: import re Then you can go to town like this: newstring = re.sub("e$", "E", s) You can also use dir() on a module. CGI scripts in Python: ---------------------- Writing CGI scripts is as easy on Python as it is in Perl. You have to use the cgi module. Remember our movie example from last time? #!/usr/local/bin/python print "Content-type: text/html" print print "" print "

Let's do this movie thing again:

" import cgi formdata = cgi.FieldStorage() movie = formdata["movie"].value if movie == "dumb": print "This is getting old.

" elif movie == "chick": print "Time to poke those eyes out again.

" elif movie == "tunes": print "Someone got mad because I picked Spinal Tap over Tommy..

" elif movie == "art": print "When was the last time you watched one of these?

" elif movie == "PRON": print "Well, at least THAT is still an option.

" else: print "I still don't know what you're talking about.

" print "" Now this is going to barf if no one set the parameter "movie". We can use exceptions to get around this: #!/usr/local/bin/python print "Content-type: text/html" print print "" print "

Let's do this movie thing again:

" import sys import cgi formdata = cgi.FieldStorage() try: movie = formdata["movie"].value except: print "Why don't you give me some form data next time?

" sys.exit(0) if movie == "dumb": print "This is getting old.

" elif movie == "chick": print "Time to poke those eyes out again.

" elif movie == "tunes": print "Someone got mad because I picked Spinal Tap over Tommy..

" elif movie == "art": print "When was the last time you watched one of these?

" elif movie == "PRON": print "Well, at least THAT is still an option.

" else: print "I still don't know what you're talking about.

" print "" Talk a Little About Homework 4 ------------------------------ blah blah blah Maintaining State On Your Server -------------------------------- When you run a program on your personal computer, with no network attached, it's pretty obvious where all of the data that this program needs to access is: somewhere on that same computer. If you're editing something, you know that whatever you're editing is in your computer's random-access memory (RAM). If the editor saves the file to the disk, you know that the file is somewhere on the filesystem that's on the disk in your machine. Things are not so obvious when you interact with the web. Clearly, when do something like submit a form, you're sending data to the web server. But where does that data go? Well, there are a couple of different things that could happen: 1. The web server could store the data in a file on one of its disks. 2. The web server could store the data in its memory. 3. The web server could store the data in another machine (through something like a relational database). 4. The web server could do some computation on the data, and send it back to the browser, possibly forgetting all about the data after the computation. 5. Any combination of the above. I think that #1 is pretty obvious. #2 is boring. I'm going to talk about #3 next time. But the really strange-sounding one is #4, and that's what I'm going to talk about today. This particular form of passing data around is kind of like a game of "hot potato:" 1. First, the web server sends a document with a form (or whatever) to the browser. 2. Then the user fills the form out or picks something on the document, and sends that data back to the web server. As soon as the browser gets a response, it forgets all about anything that got sent to it. 3. When the web server receives the new data from the browser, it analyzes it and sends a new document or some form of the old document back to the browser. 4. Go back to step 2, until the user has entered all necessary information, or is just plain tired. You can actually do quite a few things with this. In your assignment, you'll use it to play a game. Let's go through a really simple example. We want the user to enter three pieces of data: their name, their occupation, and their favorite color. However, since we don't want the poor user to have to think about too many things at once, we're going to ask them one at at time. And we're going to use the same program to generate the initial forms, and all following forms. Let's call it ask.cgi. The first thing we need to do is send a form asking the user for their name. Well, that's easy: #!/usr/local/bin/python print "Content-type: text/html" print print "" print "

" print " Hi there. What's your name?

" print "
" print "
" print "


" print "" Okay, well that's great, and when we hit Next, we see that the form data's passed on, but don't we want to do something with it? So we modify our script to see if the parameter "fullname" has been passed or not. If it hasn't, we know we're at the inital screen, so we print the initial form. But if it has, we can ask the next question, about the occupation instead: #!/usr/local/bin/python import cgi form=cgi.FieldStorage() try: fullname=form["fullname"].value except: fullname="" print "Content-type: text/html" print print "" print "
" if fullname=="": print " Hi there. What's your name?

" print "
" else: print " Nice to meet you, %s. What do you do?

" % (fullname) print "
" print "
" print "


" print "" Well, that's great, except that when we hit Next, we lost the information about the name! Remember that the web server doesn't know anything about it since it didn't save it. But we can use a trick called a hidden field when we generate the question about the occupation, to not only pass on the occupation, but the name: if fullname=="": print " Hi there. What's your name?

" print "
" else: print " Nice to meet you, %s. What do you do?

" % (fullname) print "
" print " " % (fullname) Let's say that Oderus Urungus uses this form a lot, and we want to save him some typing. We can fake a form submission with a hyperlink for just that name: if fullname=="": print "" print "Click here if you're Oderus Urungus" print "" So now we have that information, so let's move on the favorite color, doing the same thing we did with the full name. #!/usr/local/bin/python import cgi form=cgi.FieldStorage() try: fullname=form["fullname"].value except: fullname="" try: occupation=form["occupation"].value except: occupation="" print "Content-type: text/html" print print "" if fullname=="": print "" print "Click here if you're Oderus Urungus" print "

" if fullname=="": print " Hi there. What's your name?

" print "
" elif occupation=="": print " Nice to meet you, %s. What do you do?

" % (fullname) print "
" print " " % (fullname) else: print " Well that's cool. What's your favorite color?

" print "
" print " " % (fullname) print " " % (occupation) print "
" print "


" print "" Now we can finish this up by doing something a little different if the parameter color is defined. We don't want to print out the form if everything is complete - just the summary information. #!/usr/local/bin/python import cgi form=cgi.FieldStorage() try: fullname=form["fullname"].value except: fullname="" try: occupation=form["occupation"].value except: occupation="" try: color=form["color"].value except: color="" print "Content-type: text/html" print print "" if fullname=="": print "
" print "Click here if you're Oderus Urungus" if color=="": print "
" if fullname=="": print " Hi there. What's your name?

" print "
" elif occupation=="": print " Nice to meet you, %s. What do you do?

" % (fullname) print "
" print " " % (fullname) elif color=="": print " Well that's cool. What's your favorite color?

" print "
" print " " % (fullname) print " " % (occupation) else: print "

Okay, so this is you..

" print "" print " " print " " % (fullname) print " " % (occupation) print " " % (color) print "
NameOccupationFavorite Color
%s%s%s

" if color=="": print "
" print "


" print ""