[Tutor] Re: Your advice, please [raw_input(), higher order functions] (original) (raw)

Danny Yoo dyoo at hkn.eecs.berkeley.edu
Sat Jul 24 00:26:36 CEST 2004


Hi Dick,

I took a closer look at your program; there's actually a significant thing I can see that should shorten the program a bit. It has to do with the way that the code interacts with the user; in many places in your code, your program asks the user for input, and uses a fairly regular way of doing this.

Let's look at a few places where this happens. I'll look at three blocks:

Block 1:

while True: _print "If no decimal entered, a random decimal " _ "between 0.1 and 1.0 will be chosen." # for exiting via ^C or ^D try: string = rawinput("Decimal: ") except (TypeError, EOFError): break if string in ["x", "q"]: break

Block 2:

while True: choice = rawinput("Minimum error (e) or maximum denominator (d)? ") if choice in ["x", "q"]: break elif not (choice in ["e", "d"]): print "Enter d or e" continue else: break if choice in ["x", "q"]: break

Block 3:

while True: print "If no maximum denominator entered, the default is 100" maximumDenom = rawinput("Maximum denominator: ") if maximumDenom in ["x", "q"]: break elif maximumDenom == "": maximumDenom = defaultMaximumDenom print "Maximum denominator is %g by default" % maximumDenom else: try: maximumDenom = int(maximumDenom) except: print "That's not an integer! Try again." continue break if maximumDenom in ["x", "q"]: break

Each of these blocks, conceptually, does something like this:

To get user input: query the user from input if we get 'x' or 'q': let's quit the program if no answer comes at us: use a default value else if a bad answer comes at us: show an error message and ask again otherwise, use that user input as our final answer ###

The following is code that implements the pseudocode above:

def getUserInput(queryPrompt, isGoodInput, badAnswerMessage, defaultInput): """Queries the user for an input. Takes in four parameters:

queryPrompt: the prompt we pass to raw_input.

isGoodInput: some boolean function that tells us if the input looks
             good to us.

badAnswerMessage: the message we print out if the input looks bad.

defaultInput: the default value we return if the user just presses
              enter.

If no defaultInput is defined, we keep asking.  If the input is 'x' or
'q', we raise a SystemExit to quit the program.
"""
while True:
    userInput = raw_input(queryPrompt)
    if userInput in ['x', 'q']:
        raise SystemExit
    elif userInput == "" and defaultInput:
        return defaultInput
    elif isGoodInput(userInput):
        return userInput
    else:
        print badAnswerMessage

It's a little large, but most of it is commentary.

The value of writing a general function like this is that the blocks above can now use getUserInput() to do the brunt of the work of handling user input in a nice way.

Here's a quick example to show how it might work:

passwd = getUserInput("Password Please! ", ... lambda x: x == 'secret', ... 'Bad Password!', ... None) Password Please! abracadabra Bad Password! Password Please! please Bad Password! Password Please! secret

print passwd secret ###

You can ignore the 'lambda' part for the moment; we can get get back to it in a moment. But you can see that it does a lot, for just a single call to getUserInput(). And that's powerful.

For example, Block 3, which looked like:

while True: print "If no maximum denominator entered, the default is 100" maximumDenom = rawinput("Maximum denominator: ") if maximumDenom in ["x", "q"]: break elif maximumDenom == "": maximumDenom = defaultMaximumDenom print "Maximum denominator is %g by default" % maximumDenom else: try: maximumDenom = int(maximumDenom) except: print "That's not an integer! Try again." continue break if maximumDenom in ["x", "q"]: break

can be reduced a single call to getUserInput() and a definition of a function that tells us if we're looking at an integer.

def looksLikeInt(value): """Returns True if the value looks like an integer, and otherwise returns False.""" try: int(value) return True except ValueError: return False

maximumDenom = int(getUserInput("Maximum denominator: ", looksLikeInt, "That's not an integer! Try again." defaultMaximumDenom)) ###

Using getUserInput() is a little weirder than using a straightforward raw_input(), but it does have versatility. The key part of this is the following: we have to pass it some notion of what a good answer looks like, so that it knows when to keep asking.

In the example above, we wrote a quick-and-dirty 'looksLikeInt()' function, and then passed that off to getUserInput(), so that getUserInput() can know what we think a satisfactory answer looks like.

In Block 2, we can do something similar:

def isErrorOrDenominatorChoice(value): return value in ['e', 'd']

choice = getUserInput("Minimum error (e) or maximum denominator (d)? ", isErrorOrDenominatorChoice, "Enter d or e", None) ###

And again, we write a quick-and-dirty function to tell the system that 'e' or 'd' is a good value to accept, and pass it off to getUserInput().

Does this make sense so far?



More information about the Tutor mailing list