[Tutor] Re: Your advice, please [raw_input(),
higher order functions]
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 = raw_input("Decimal: ")
> except (TypeError, EOFError):
> break
> if string in ["x", "q"]:
> break
Block 2:
---
> while True:
> choice = raw_input("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 = raw_input("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 = raw_input("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