Python Patrol Tutorial
Copyright (C) 1999 by Steve Litt, All rights reserved. Material provided
as-is, use at your own risk.
Python Basics
Check that Python is Installed
Do this command:
python
You should see a message and prompt similar to this:
Python 1.5.1 (#1, Sep 3 1998, 22:51:17) [GCC 2.7.2.3] on linux-i386 Copyright 1991-1995 Stichting Mathematisch Centrum, Amsterdam >>>
|
If that message does not appear, do what it takes to get python installed
and accessible.
Hello World
Create a file called hello.py. It should look like this:
Be absolutely sure not to have any spaces to the left of the word
"print". Python uses indentation sensitive syntax, and will fail if
there are any spaces or tabs to the left of the print statement.
NOTE
For Python 3.x, the print statement would be a function, so it would look like this:
print("Hello World")
All 2.x print statements are functions in 3.x:
print("I have quantity ", quantity, items on hand.") More
print differences will be noted in this document as Python 3.x comes
into greater usage. As of April 2009, this document is written for
Python 2.x, as that's what's in most Linux distributions.
|
Run it with this command:
python hello.py
It should print the words "Hello World" to the screen. If it doesn't, make
sure python the file you made is called hello.py, it's in the current directory,
it contains the text shown in the box above, and contains no space before
the command.
Make it an Executable
For many uses, including CGI, your program must be self-executing. First,
add a line to the top of the file telling the OS to run the program with
Python, and telling it where to find Python, as shown below:
#!/usr/bin/python print "Hello World"
|
The top line assumes that your Python executable is in /usr/bin. If
it's somewhere else, substitute that path.
Now make the file executable with this command:
chmod a+x hello.py
Finally, run the command like this:
./hello.py
It should print the words "Hello World" to the screen. If not, troubleshoot.
Notice that a semicolon is NOT required at the end of the line. Semicolons
are necessary ONLY to separate multiple commands on the same line. Otherwise,
the end of the line ends the command.
Add a Variable
Insert code to add variable myname, and print it.
#!/usr/bin/python myname = "Steve Litt" print "Hello World" print myname
|
This should print a line saying "Hello World", followed by a line saying
"Steve Litt" to the screen. If not, troubleshoot.
Notice these things:
-
It prints newlines automatically. If you wish to suppress the newline printing,
end the print line with a comma, in which case it prints an extra space
but no newline.
-
To avoid the space at the end of the print, do string concatenation.
Add Comments
#!/usr/bin/python myname = "Steve Litt" #This is a comment # So is this #And this is a comment print "Hello World" #This is an inline comment print myname
|
Note the following:
-
Comments begin with a pound sign, hash symbol, #
-
Comments alone on their own lines are not subject to indentation sensitive
syntax.
-
Inline comments are preceeded to a pound sign, and are to the right of
an executable.
Simple String Concatenation
#!/usr/bin/python longname = "Anderson" print longname longname = longname + "berg" print longname
|
This program should print a line saying "Anderson", followed by another
line saying "Andersonberg". If not, troubleshoot.
Python can do incredible things with strings, including all the regular
expression activities as seen in Perl. These will be presented later in
this tutorial.
Create a Loop and Handle Indentation
Create a new file called loop.py, put the following text into it, and chmod
it executable. Run it as ./loop.py.
#!/usr/bin/python ss = 1 while ss < 10: print "Inside loop, subscript is", ss ss = ss + 1 print "Finished looping."
|
This should print out nine lines stating the subscript, and a final
line saying "Finished Looping". If anything goes wrong, make sure the while
statement ends with a colon and check your indentation.
Test at Top
The while loop tests at the top, meaning the test var must be set before
entry to the loop to determine whether the loop will be entered at all.
Testing at the bottom can be simulated by incrementing at the top of the
loop, meaning the new value will be processed regardless of the results
of the test on the new value. Test at the bottom also can be accomplished
with break statements, as will be discussed later in this tutorial.
Indentation Sensitive Syntax NOTE THIS
Python has no begin and end statements. In Python, program flow statements
(if, for, while) and subroutine definitions (def) end when indentation
returns to its previous level. Indentation errors will cause the program
to fail. Let's use the loop.py program as an illustration.
Everything intended to be subservient to the while statement (in other
words, all the statements to be looped through), should be indented a uniform
amount to the right of the while statement. That indentation can be either
hard tabs, or (my preference), a certain amount of spaces. For the sake
of future maintainers of your code, use one or the other consistently.
As a generality, whenever a Python statement ends in a colon (like the
while statement above), it's expected to have subservient statements, and
those subservient statements must be consistently indented relative the
statement with the colon.
Advantages of Indation Sensitive Syntax
Those of us educated in the 1980's and 1990's couldn't help but notice
the contempt of our instructors for computer languages depending on indentation.
There were chuckling references to Cobol and RPG. We were taught that in
this brave new world of C, and all the following code would run:
if (i < 10)
{
printf("Less than 10\n");
i = 20;
}
or
if (i < 10)
{
printf("Less than 10\n");
i = 20;
}
or
if (i < 10){
printf("Less than 10\n");
i = 20;
}
or
if (i < 10){
printf("Less than 10\n");
i = 20;
}
or even
if (i < 10) {
printf("Less than 10\n");
i = 20;
}
Then we were urged to write readable code, even though the compiler would
accept almost anything. Without the compiler to catch indentation mistakes,
succeeding generations of maintenance programmers slowly made the code
unreadable. Not so with Python. The above if statement would be ultra
readable:
if (i < 10):
printf("Less than 10\n")
i = 20
No bracket matching, no strange indentation -- everything in a block looks
like a block.
I'm not saying indentation sensitive syntax is perfect. It can be maddening
to fail because of an extra space. But in these days of inadequate documentation,
the simplicity and self-documentation of Python syntax can be welcome.
Insert an If Statement Inside the Loop
#!/usr/bin/python ss = 1 while ss < 10: if ss % 3 == 0: print "Inside loop, subscript is", ss, print "Divisible by three" elif ss % 2 == 0: print "Inside loop, subscript is", ss, print "Divisible by two" else: print "Inside loop, subscript is", ss, print "Divisible neither by three nor two" ss = ss + 1 print "Finished looping."
|
This should put out the following:
Inside loop, subscript is 1 Divisible neither by three nor two
Inside loop, subscript is 2 Divisible by two
Inside loop, subscript is 3 Divisible by three
Inside loop, subscript is 4 Divisible by two
Inside loop, subscript is 5 Divisible neither by three nor two
Inside loop, subscript is 6 Divisible by three
Inside loop, subscript is 7 Divisible neither by three nor two
Inside loop, subscript is 8 Divisible by two
Inside loop, subscript is 9 Divisible by three
Finished looping.
If there are problems, troubleshoot. Pay special attention to indentation
and colons
Note the following:
-
If statements can be followed by any number of elif statements (else
if), and one else statement. If, elif and else all have colons, and statements
to all three are indented relative to the if, elif or else.
-
Python's indentation and lack of block specifiers makes for much more readable
code.
Use the Pass Statement
Occasionally you may want an empty if or else statement. Python cannot
accommodate an empty statement because it has no block enders like Pascal,
C, Java, etc. So Python gives us the pass statement, whose purpose is to
serve as an empty statement. Consider this:
#!/usr/bin/python month = 3 #var in if statement must have been declared if month == 1: pass elif month < 6: print "early" else: print "late"
|
Here January does nothing, months Feb thru May print "early", and months
June thru December print "late". This is not a particularly good example,
but this is how the pass statement is used.
Use the break and continue Statements
Break statements immediately leave the loop.Continue statements abandon
the remaining statements in the loop and skip back up to the test. Both
are violations of structured programming, which states a control
structure
should have one exit point. Both are close cousins of the dreaded goto
statement (which Python doesn't have). Both can create very harsh
maintenance
problems later. In this author's opinion, the break statement has more
practical uses than continue, but even continue is occasionally useful.
An example would be having a loop ignore blank lines:
#!/usr/bin/python import sys import string
for line in sys.stdin: line = string.rstrip(line) if(string. (line) == ""): continue print line # do lots of stuff with nonblank lines |
The break statement can sometimes improve readability
and even maintainability. This is especially true when two different conditions
can stop the action. Create this break.py program, make it executable and
execute it. Note that the strategic placement of the break statement prevents
an extra comma after the last number. Note that the while 1 would go on
forever if not for the break statement.
#!/usr/bin/python ss = 1 while 1: print ss, if(ss == 9): break; print ",", ss = ss + 1 print "."
|
The output of this program should be:
1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 .
If it isn't, troubleshoot.
Exceptions
If
you began life as a C programmer, exceptions are a difficult concept to
grasp and seem like a logic cluttering pain. The nice thing about
exceptions is they move runtime errors from wierd results to orderly
program termination, with a readable error message. For the most part,
if you program carefully, you can get away without exception handling,
secure in the knowledge that if something unusual goes wrong, the
program will terminate with an informative error message.
Sometimes you have to use exception handling. An example is the list.pop()
function. It returns the highest-indexed member of the list, and
eliminates that member from the list. However, if it's performed on an
empty list, it doesn't return EOF or some other testable quantity. So,
unless you want to count the number of items on each iteration, the
only way to stop on the last legal list.pop() is to test for exceptions. As a starting point, observe the following program:
#!/usr/bin/python numbers = [] numbers.append("one") numbers.append("two") numbers.append("three") numbers.append("four") numbers.append("five")
for number in numbers: print number
while(1): print numbers.pop()
print "This is the end of the program and should print!" |
The following output comes from runing the preceding program:
[slitt@mydesk python]$ ./test.py one two three four five five four three two one Traceback (most recent call last): File "./test.py", line 13, in <module> print numbers.pop() IndexError: pop from empty list [slitt@mydesk python]$ |
Notice
that the preceding output is exactly right until it tries to pop from
an empty list and throws an exception that terminates the program. The
final statement doesn't print. This is not acceptable, because the
program is supposed to pop
the list down to empty. Repeating, there's no way other than repeatedly
testing the size of the list, to know when it's empty and prevent that
final pop. So instead, use try, except and else exception handling:
#!/usr/bin/python numbers = [] numbers.append("one") numbers.append("two") numbers.append("three") numbers.append("four") numbers.append("five")
for number in numbers: print number
while(1): try: number = numbers.pop() except: break else: print number
print "This is the end of the program and should print!" |
The preceding code does the right thing:
[slitt@mydesk python]$ ./test.py one two three four five five four three two one This is the end of the program and should print! [slitt@mydesk python]$ |
The try: block contains the code that might fail, in this case list.pop(). Typically it contains file opens or file reads. It does not contain code that uses the return of the might-fail code.
The except: or except ExceptionName:
block contains what you do if the attempted code fails. If the attempt
was to open a file, it might try a different file, or just shut down
gracefully. If the failed code were user input, it would print an error
message and do a continue, maybe like this:
#!/usr/bin/python
import sys import string
class YornError(Exception): pass
def yorn(): line = sys.stdin.readline().upper().rstrip().lstrip() if line == "YES": pass elif line == "NO": pass else: raise YornError; return line
while(1): try: line = yorn() except YornError: sys.stderr.write("Must type \"YES\" or \"NO\".\n") continue else: break
print "User chose", line
|
In the preceding you defined a new exeption, YornError, as a subclass of Exception. You raise that exception from within the yorn() function if the user types anything but yes or no. The loop calling yorn() uses that exception to do the right thing.
The
preceding code is rather contrived, and there are easier ways of
forcing a "yes" or a "no", but it illustrates one way to use exceptions.
We've listed exception handling blocks try:, except:, and else:. There's one other, :finally that is sometimes useful. The finally: block contains everything that must be done whether the try: block succeeds or fails. It's cleanup code.
Avoiding Obnoxious Exception Nesting
Imagine if the else: block contained a whole nother try: except: else: exception, and that else: block contained yet another try: except: else: exception, and yet again and again. Ugly! And all this nesting just for error handling.
Often
you can simply let the exception handling "fall through" to the code
you normally want to execute, instead of putting that code in the else: block. This can be done when the except: block prevents further execution, such as when the except: block ends with a break or continue statement within a loop, or an exit() call any time. The following code is just like previously shown code, except the print number command has been removed from the else: block and put after all exception handling. Because the exception breaks, the print number statement won't be printed once the exception has been handled.
#!/usr/bin/python numbers = [] numbers.append("one") numbers.append("two") numbers.append("three") numbers.append("four") numbers.append("five")
for number in numbers: print number
while(1): try: number = numbers.pop() except: break print number
print "This is the end of the program and should print!" |
Exception Handling Summary
You can create a new exception by subclassing an existing one:
class YornError(Exception): pass |
In the preceding, the new exception is YornError, it's subclassed from root exception Exception, and in itself it does nothing due to the pass being the only thing it does.
You can raise an exception, whether built in or user-created, with the raise keyword:
When handling exceptions from code that might fail, you use these three blocks:
try: | The code that's likely to fail. | | | |
except: | Code
to execute if the tried code raises an exception. The except: block can
list one or more exceptions, or it can list none in which case ANY
exception triggers the except: block. | | | |
else: | The code to execute if the tried code succeeded. | | | |
A fourth block, finally:, happens whether the tried code raises an exception, and happens after the except: and else: blocks have been executed. It's executed even if the except: or else: code terminates the program.
x
Lists
Hello World
Create, make executable and run this hello.py:
#!/usr/bin/python seasons = ["Winter","Spring","Summer","Fall"] print seasons
|
The output will be:
['Winter', 'Spring', 'Summer', 'Fall']
If not, troubleshoot.
What you have done here is created a list. Lists are mutable (changable),
both in terms of changing individual members and in terms of adding, deleting
or rearranging members.
Accessing List Members
Now run this program:
#!/usr/bin/python seasons = ["Winter","Spring","Summer","Fall"] print len(seasons) #there are 4 seasons print seasons[0] #element 0, the 1st element print seasons[2] #element 1 print seasons[len(seasons)-1] #last element print seasons[-1] #last element print seasons[0:2] #elements 0 and 1, (1st & 2nd) print seasons[1:3] #elements 1 and 2 print seasons[1:9] #1 thru last, overrange not error print seasons[-1:9999] #last element print seasons[-1:len(seasons)] #last element print seasons[-3:-2] #3rd &2nd to last elements
# print seasons[len(seasons)] would produce an error, as # the last element is seasons[3].
|
-
len(listname) returns the number of elements in the list. Since the list
is zero based, the number of elements equals one plus the index of the
last number.
-
listname[x] represents the xth member of the list, where the first member
is listname[0].
-
listname[s:e] is called "slicing" the list, and represents a subset list,
of all listname elements with indexes s <= x < e. In other words,
starting with the s element of the original list, and containing all members
up to, but not including, the e member. Please remember all these subscripts
are zero based. If the ending number is too big, it simply delivers the
remainder of the list including and after element s.
-
listname[-s:-e] is the a subset list starting with the member whose index
len(listname) - s, and ends BEFORE len(listname) - e.
-
Other combinations are possible. Experiment.
-
Accessing a single element beyond the upper limit produces an error.
Looking Up List Members by Value
#!/usr/bin/python seasons = ["Winter","Spring","Summer","Fall"] n = seasons.index("Summer") print n print seasons[n]
|
Use this with care. If the argument to index is not a value in the list,
the program fails to run. also, index pulls up only the first element with
that value. To be sure all such elements have been deleted, use listname.count(string)
to get a count of the number of times it appears, then run through a loop
to get them all. Note also that this prevents a non-occurring string from
bombing the program.
Inserting List Members
Using the insert list method, members can be inserted anywhere in a list,
including the beginning or the end.
#!/usr/bin/python seasons = [] seasons.insert(len(seasons), "Spring") #append to empty list seasons.insert(0, "Winter") #insert at beginning of list seasons.insert(len(seasons), "Fall") #append to end of list print seasons seasons.insert(2, "Summer") #insert before seasons[2], "Fall" print seasons
|
In this exercise, you inserted elements at the beginning, at the end,
and in the middle.
Appending List Members
#!/usr/bin/python seasons = [] seasons.append("Winter") #append element print seasons seasons = seasons + ["Spring", "Summer"] #append a list to a list print seasons seasons.append("Fall") #append element print seasons
|
In this exercise you used the append command to append elements, and
you used the plus (+) operator to append an entire list.
Deleting a List Member
#!/usr/bin/python seasons = ["Winter","Spring","Summer","Fall"] print seasons del seasons[2] print seasons seasons = ["Winter","Spring","Summer","Fall"] print seasons del seasons[1:3] print seasons
|
Add a For Loop
#!/usr/bin/python seasons = ["Winter","Spring","Summer","Fall"] for x in seasons: print x, "season."
|
The output here should be:
Winter season.
Spring season.
Summer season.
Fall season.
Practical Exercise: A Data Generator
This code outputs 25 different names, with phony numbers, to stdout. This
can be used as test data for a program.
#!/usr/bin/python
lastnames = ["Albright","Barclay","Chavez","Dugan","Eng"] firstnames = ["Allen","Betty","Charles","Donna","Edward"] empno = 1001 outstring = ""
for first in firstnames: for last in lastnames: print "%04d,%s,%s" % (empno, last, first) #comma delimeted empno = empno + 1
|
Note the following:
-
Nested for loops
-
Enhanced print statement very much like C's printf(). Note the use of the
percent sign between the format string and the list of args, and the fact
that the args are in parens.
-
Slightly modified, this program is actually useful for producing test data.
It's 11 lines, counting blank lines.
-
With a name per alphabet letter, and a list of middle names (another nest
in the loop), it can output 26*26*26 different names, for a total of 17,576
records.
Stack Operations
Strings
#!/usr/bin/python mystring = "house" print "Original string", mystring mystring = mystring + "keeper" print "After appending keeper:", mystring print "Letters 0 to but not including 4:", mystring[0:4] print "Letters 5 to but not including 9:", mystring[5:9]
|
Dictionaries
Python dictionaries are like Perl
hashes. They're associative arrays. They're a bunch of key->value
pairs contained in one variable, the dictionary. Here's a dictionary
hello world:
x
#!/usr/bin/python billsmith = {"fname": "William", "lname": "Smith", "jobdesc": "Software developer"} print "Employee", billsmith["fname"], billsmith["lname"], "is a", billsmith["jobdesc"] |
You could have loaded billsmith one at a time:
#!/usr/bin/python
billsmith = {} billsmith["fname"] = "William" billsmith["lname"] = "Smith" billsmith["jobdesc"] = "Software Developer"
print "Employee", billsmith["fname"], billsmith["lname"], "is a", billsmith["jobdesc"] |
Another cool use of hashes is finding unique items in a list:
#!/usr/bin/python
mystring = "BILL GOT THE BILL FOR THE SQUARE FOR THE PEOPLE" myarray = mystring.rsplit() mydictionary = {} for word in myarray: try: mydictionary[word] = mydictionary[word] + 1 except: mydictionary[word] = 1
keys = mydictionary.keys() keys.sort() for key in keys: print "Word", key, "has", mydictionary[key], "occurences."
|
The preceding code produces the following output:
[slitt@mydesk python]$ ./test.py Word BILL has 2 occurences. Word FOR has 2 occurences. Word GOT has 1 occurences. Word PEOPLE has 1 occurences. Word SQUARE has 1 occurences. Word THE has 3 occurences. [slitt@mydesk python]$ |
You'll
learn a lot by looking over the preceding code and output. A string is
split into an array. The words of the array are ringtossed into a hash,
counting the occurrences. Because you can't increment something that's
not there yet, the exception creates an item for the new word and
gives it an occurrence of 1.
To iterate over the words in the
dictionary, a list of keys is derived and then sorted. Note that sort()
occurs in place and doesn't return a list, so this must be two
different statements. Finally, the keys are iterated and listed with
their occurrences.
In Memory Data Table
Here's an in-memory implementation of a data table with a numerical primary key:
#!/usr/bin/python
#### BEGINNING OF GENERIC DATA TABLE FUNCTIONS #### def newtable(firstsubscript): table = {} table["lastss"] = firstsubscript return table
def addrow(table, row): table["lastss"] += 1 table[table["lastss"]] = row
def delrow(table, row): if table["lastss"] < row: sys.stderr.write("Key too high:", row, "!\n"); elif table["lastss"] == row: del(table[row]) table["lastss"] -= 1 elif table.has_key(row): del(table[row]) else: sys.stderr.write("No such key:", row, "!\n");
def listrows(table): keys = table.keys() keys.sort() for key in keys: if(key == "lastss"): continue print "Row", key, "is", keys2 = table[key].keys() keys2.sort() displaystring = "" keyno = 0; for key2 in keys2: if keyno != 0: displaystring += ", " keyno += 1 displaystring += (key2 + "=>" + table[key][key2]) print displaystring #### END OF GENERIC DATA TABLE FUNCTIONS ####
#### BEGINNING CREATION ROUTINE FOR SPECIFIC TYPE OF DATA ROW (PERSON) #### def newperson(fname, lname, jobdesc): person = {} person["fname"] = fname person["lname"] = lname person["position"] = jobdesc return person #### END CREATION ROUTINE FOR SPECIFIC TYPE OF DATA ROW (PERSON) ####
#### START OF MAIN ROUTINE #### # Instantiate new table, first key will be 1001 people = newtable(1000)
# Add Rows addrow(people, newperson("Barak", "Obama", "President")) addrow(people, newperson("John", "McCain", "President")) addrow(people, newperson("Linus", "Torvalds", "Linux Maintainer"))
# Oops, McCain wasn't president, so delete his record, 1002. # But we've already gone past 1002, so just leave 1002 vacant delrow(people, 1002)
# Add more rows addrow(people, newperson("Joe", "Biden", "Vice President")) addrow(people, newperson("Sarah", "Palin", "Vice President"))
# Oops, Palin wasn't vice president, so delete her record, 1006. # 1005 was the last created, so just set last subscript back to 1004 delrow(people, 1005)
# Add another row, which takes the place of Palin's addrow(people, newperson("Tiger", "Woods", "Golfer"))
# List all the rows listrows(people) |
The
preceding, which admittedly would have been much better as an OOP
class, implements four generic table handling functions: newtable(), addrow(), delrow() and listrows(). The newperson() function creates a specific type of record, a "person" with attributes fname, lname and position.
The delrow()
function takes the highly controversial practice of, when deleting the
last row, simply setting it so that row is used again on the next addrow(). Such reuse is not enabled when the row to be deleted is not the last row.
The table
entity keeps track of its rows with integer dictionary keys. However,
its metadata is contained in non-integer keys, the only one of which in
this implementation is lastss, which refers to the subscript (primary key) of the latest add.
So
this table is implemented as a dictionary of dictionaries. If deletion
weren't an option, and it would be OK to start the primary keys with 0,
it could have been implemented as an array of dictionaries.
xx
xx
xx
xx
xx
xx
xx
xx
xx
x