User Input

    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

    Index