do you guys help newbies??
James T. Dennis
jadestar at idiom.com
Wed Nov 27 15:01:44 EST 2002
malik m <malik_martin at hotmail.com> wrote:
> hi yea i messed up here's the code sorry about the html too i was
> using it through hotmail while i learnd and still am learning how to
> use newsgroups....
> #chapter 3 test control structures 3.3
> print "hello, this program will tell me how many miles per gallon your
> car drives by the \n amount of miles entered by the user along with
> the size of his/her tank."
Clearly this is From Deitel(TM)'s _Python:_How_To_Program_
That's a book that spoonfeeds the prospective programmer. It's not
held in high regard among Pythonistas.
One of my complaints is that the text is pretty unclear (at least in
the early chapters) about the nature of Python's exception handling.
In several places the Dietel book refers to various conditions which
"cause a program to terminate with an error" (or words to that effect).
In most books on Python the authors make it clear that exception handling
is a fundamental control structure in Python. Thus it should have been
covered in this chapter. Also all references to abnormal terminations
from programming or data errors should refer to the specific exception
that is being raised.
(That critique is for the rest of the readership: Let me explain for
you, Malik: When you make a mistake in Python, such as divide by
zero or try to use a string as a number then Python will "raise an
exception." By default exceptions are reported as an error, and
the program exits at that point. When a Python program terminates
due to an exception, it prints some debugging information called a
traceback. Good books on Python programming explain this in the first
or second chapter. When ever you're trying to get help with a python
script in a newsgroup or on a mailing list, you should post the plain
text of a *small and relevant* fragment of your code and, if possible
a capture of the traceback. (Under UNIX a quick way to capture the
traceback is to use a command like: foo.py 2> ~/tmp/foo.err; that is
redirect 'stderr' to a file). It's also possible for your program to
catch specific types of exceptions that you know might be caused by
specific blocks of your code. This is perfectly normal under python
and should NOT be avoided. However it should be done carefully, so
you don't make your programming errors harder to find).
> #initialize variables
> totalMilesDriven = 0 #total miles driven
> totalGallonsPerTank = 0 #total gallons per tank
> counter = 0 #bogus counter
> #processing phase
> gallonsPerTank = raw_input("Enter how many gallons your tank holds, -1
> to end:")
> gallonsPerTank = int(gallonsPerTank)
This should be something more like:
try:
gallonsPerTank = int(gallonsPerTank)
except ValueError:
gallonsPerTank = -1
# Take an invalid input as a call to exit.
... or (better yet):
TankSize = "Enter how many gallons your tank holds, -1 to end:"
while 1:
gallonsPerTank = raw_input(TankSize)
try:
gallonsPerTank = int(gallonsPerTank)
break
except ValueError:
print "Invalid Response! Please try again."
(Actually this code is still a little weak because it will reject
floating point numbers. But it will have to suffice for pedagogical
purposes).
> milesDriven = raw_input("enter how many miles driven, -1 to end:")
> milesDriven = int(milesDriven)
See above. You have to validate your inputs.
Generally it's wise to isolate you input validation code into
a function. This allows your main program code to focus on
the main task at hand (hiding the details of validation from maintainers
and readers of that code) and helps reduce the chance that you'll
duplicate that code in multiple places in your program (as you've
done below, within the same prompts and raw_input functions inside
your while loop).
Actually it's even better to create a class (object template). That
class (or functions within it) would then be responsible for it's own
validation. However, that is probably overkill for such a simple
exercise. The question becomes, "who" (which object/class) would be
responsible for the actual inputs. I don't like the idea of hiding
the raw_input() calls inside of an object's initializer. Of course
raw_input() is not used much in real Python programming because usually
some other interface is being implemented (CGI forms processing, GUI,
network, file, or database, for some examples).
I'm not going to speculate on an OO design for this exercise.
> while gallonsPerTank or milesDriven != -1:
This looks wrong. I think you mean to say:
while gallonsPerTank != -1 and milesDriven != -1:
... Because your conditional expression will be short-circuited
any time gallonsPerTank set bound to *any* value other than a Python
"false" (numeric zero, empty string, tuple, etc). In any other case
(gallonsPerTank is set to any non-zero or non-empty value) then
the next expression will be evaluated and that result will be returned
for the expression as a whole.
So your loop won't exit until gallonsPerTank is 0 (or empty) and
milesDriven is anything other than -1.
> averagea = milesDriven / gallonsPerTank
Most people want floating point (non-integer) results from averages.
So:
averagea = float(milesDriven) / gallonsPerTank
... will *coerce* (force) the whole expression to return a floating
point value.
Normally I'd complain about your failure to check for gallonsPerTank
being zero. Ironically the erroneous while condition actually guards
against that. However, when you change the while condition to something
that expresses your intent, then you could have a case where
gallonsPerTank == 0
You can handle this one of three ways:
1) use a conditional before the division:
if gallonsPerTank:
averagea = float(milesDriven) / gallonsPerTank
else:
### What?
import sys
averagea = sys.maxint
### You got infinite mileage from "no gas"?
2) use Python's exeption handling?
try:
averagea = float(milesDriven) / gallonsPerTank
except ZeroDivisionError:
import sys
averagea = sys.maxint
### See above about this absurdity.
... I think you'll see the problem with both of these approaches.
You can't have a valid mpg calculation if you had NO gasoline.
That leaves us with:
3) treat 0 (zero) as an invalid value for gallonsPerTank.
(Go back to your gPT input function and change it to something like:
while 1:
gallonsPerTank = raw_input(TankSize)
try:
gallonsPerTank = int(gallonsPerTank)
if gallonsPerTank == 0:
print "Can't get any mileage with no gasoline"
print "... unless you're on a bicycle"
print "... or in a rowboat ;)"
continue
break
except ValueError:
print "Invalid Response! Please try again."
> counter = counter + 1
> print "your miles per gallon are", averagea
> totalMilesDriven = totalMilesDriven + milesDriven
> totalGallonsPerTank = totalGallonsPerTank + gallonsPerTank
Don't you mean just totalGallons or totalGallonsConsumed?
> gallonsPerTank = raw_input("Enter how many gallons your tank
> holds, -1 to end:")
> milesDriven = raw_input("enter how many miles driven, -1 to end:")
See above for my comment on duplicating your input code and thus
having multiple places where you'd need to maintain any validation
code. This is called "cut & paste" programming --- and is a BAD thing.
Here's sample function for you to use:
def GetGallonsPerTank(prompt="Enter tank capacity in gallons, -1 to exit"):
while 1:
response = raw_input(prompt)
try:
r = int(response)
if r <= 0:
print "Are you on a bicycle?"
continue
break
except ValueError:
print "Must enter an integer"
return r
... (untested).
If we create another input function for GetMilesDriven() it will
be almost the same. That's irritating (more code duplication).
If the code were *exactly* the same we could use one function for
both inputs. If there's enough similarity (and a reasonable assurance
that the validation requirements for the different variables won't
diverge too much in the future, we could encapsulate the differences
into parameters or help functions and pass those to our common function
as arguments.
So we might have:
def GetValidInput(prompt, validate, errormsg):
while 1:
response = raw_input(prompt)
try:
return validate(response)
except ValueError:
print errormsg
... along with functions for ValidateTankCapacity() and ValidateMileage()
which might look something like:
def ValidateTankCapacity(proposed):
r = int(proposed)
if r <= 0: raise ValueError
return r
... etc.
You'd call this with statements like:
inputPrompt = "Enter Gas Tank Capacity, -1 to exit: ",
invalidMsg = "Must enter a whole number!"
tankCapacity = GetValidInput(prompt, ValidateTankCapacity, invalidMsg)
inputPrompt = "Enter Mileage Driven, -1 to exit: ",
mileageDriven = GetValidInput(prompt, ValidateTankCapacity, invalidMsg)
Again, all of this is probably overkill for this exercise.
> #termination phase
> if counter != 0:
> average = float( totalMilesDriven ) / float( totalGallonsPerTank )
Only need to coerce one of these to a float.
> print "The total miles per gallon is", average
> else:
> print "No data entered"
More information about the Python-list
mailing list