Troubleshooters.Com, Code Corner and Python Patrol Present

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:
 
print "Hello World"

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:

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:

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:

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:

raise YornError;

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

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:

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]

#!/usr/bin/python
#!/usr/bin/python
#!/usr/bin/python
#!/usr/bin/python
#!/usr/bin/python
#!/usr/bin/python
#!/usr/bin/python
#!/usr/bin/python
#!/usr/bin/python
#!/usr/bin/python
#!/usr/bin/python
#!/usr/bin/python
#!/usr/bin/python
#!/usr/bin/python
#!/usr/bin/python
#!/usr/bin/python
#!/usr/bin/python
#!/usr/bin/python
#!/usr/bin/python
#!/usr/bin/python
#!/usr/bin/python
#!/usr/bin/python

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


Troubleshooters.ComCode Corner * Python Patrol * Linux Library