[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
- Previous message: [Tutor] A bit long, but would appreciate anyone's help, if time permits!
- Next message: [Tutor] os.popen() questions
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
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 badAnswerMessageIt'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?
- Previous message: [Tutor] A bit long, but would appreciate anyone's help, if time permits!
- Next message: [Tutor] os.popen() questions
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]