This case study looks at inheritance with a small set of classes to handle user input.
Input from the user may be done in a couple of ways in Python. The function
raw_input may be used to input a string and input is
commonly used for numbers. Actually any Python expression may be input.
In this study we will take a somewhat different approach. First some background.
Digital Equipment Corp. (DEC) has had a successful family of mini computers that began with the PDP-11 in the 1970's, extended to the VAX series in the 80's and 90's, and today is represented by the DEC ALPHA. Several of the operating systems on these machines have an interesting feature called indirect files.
Using the correct call to the OS, the system prompts the user for input. If
the user inputs a response, it is returned. If the user inputs the end of file
character (Cntl-Z on these systems) it is a signal that the program should
exit. But if the user responds with @
followed by a filename, it
indicates that the program should open this (indirect) file and read a line
which becomes the users response. Further calls for input read successive lines
from the file until there are no more. Then the user is prompted again to
input from the console. This general and fairly simple mechanism made it
possible for almost all programs to be easily scripted.
While input is taken from the indirect file nothing is printed on the user terminal. In addition, the indirect file may also contain a reference to another indirect file. This nesting may be arbitrarily deep.
We will build a little set of classes to emulate this behavior plus a couple of additional features. Click here to see the full Python source.
The class InputString provides a method get that to get input
from the user of indirect file. The filStack list contains a stack of open file
instances of indirect files are currently active. If none are open, filStack is
an empty list. The method get uses a while loop to read lines
from either the user or the current indirect file until it has a valid line to
return, or the user has input the end of file character.
There are two additional features to our class. When the InputString object
is created the option Echo=1 may be included to force prompts and
responses to be printed whether coming from the user or indirect files. The
default Echo=0 inhibits this for indirect files. The second
feature allows comments to included which are skipped over, that is, not passed
back to the program. A comment is any line begining with the #
character.
Here is a snippet of code showing how the class is used
inp = InputString() while 1 : lin = inp.get() if lin == "" : break ...
Notice that filStack is defined within the class definition but outside of any method.
class InputString : filStack = [] def __init__ (self, Echo=0) : ...
This makes filStack a class variable, a single variable accessible
to all class instances as self.filStack. This means that
subclasses of InputString will all read from the same set of files. There is
only one user.
Next we subclass InputString and make InputWord. Each call to its
get method will return the next word from either direct or
indirect input. It uses InputString.get to retrieve a line, splits it into a
list of words, and returns the next word each time it is called.
Finally we subclass InputWord to make InputNumber. This is fairly simple
since words are simply replaced by numbers in the input. We use the
string.atoi function to convert them. The empty string is checked
for (end of file from user) and passed through. Otherwise the word returned is
converted to a integer and returned.
As an example we'll build a spell checker in python using a set of files
containing correctly spelled words to fill a dictionary. These files may be
retrieved from spell.tar.gz. Here is a bit of
dict.w
wacky wade waded wad-cutters wading waffling wage wage-price waged wages waging wagon wagons wail wailed waist waistband waists wait waited waiter waiters waiting waitress waits waive waived waiver ...
And here is the program code
inp = InputWord () words = {} while 1 : w = inp.get("Input Word to fill: ") if not w : break words[w] = 1 print while 1 : w = inp.get("Input Word to check: ") if not w : break if not words.has_key(w) : print w, "Misspelled, Try Again"
We will make an indirect file of word files named words.all that looks like this.
@dict.a @dict.b @ ...
And finally we run the code above. Note that ^D is a
single character (Control-D).
Input Word to fill: @words.all Input Word to fill: ^D Input Word to check: now is the time for all good men Input Word to check: to hellp their parrty hellp Misspelled, Try Again parrty Misspelled, Try Again Input Word to check:
Copyright (c) 2001 Chris Meyers