Not graded. So why do it?
Not graded. So why do it?
Not graded. So why do it?
Not a number
Here's the code again.
- # Compute tip for a meal.
- # By Kieran Mathieson, June 14, Year of the Dragon
- import sys
- # Input
- meal_cost = float(input('Meal cost? '))
- service_level = input('Service level (g=great, o=ok, s=sucked)? ')
- # Normalize service level
- service_level = service_level.strip().lower()
- # Validate service level
- if service_level != 'g' and service_level != 'o' and service_level != 's':
- print('Please enter G, O, or S.')
- sys.exit()
- #Processing
- if service_level == 'g':
- tip_rate = 20
- elif service_level == 'o':
- tip_rate = 15
- else:
- tip_rate = 10
- tip = meal_cost * tip_rate / 100
- total = meal_cost + tip
- # Output
- print('-----------------')
- print('Meal cost: ' + str(meal_cost))
- print('Tip rate: ' + str(tip_rate) + '%')
- print('Tip: ' + str(tip))
- print('Total: ' + str(total))
We've validated service_level
. But what about meal cost?
Run the program again, but this time type a word for meal cost, instead of a number. Like "doggos".
What happened?

Adela
I got an error: ValueError: could not convert string to float: 'doggo'
Why did you get the error?

Adela
Oh, I see! The float()
function tried to convert a word into a number.
- meal_cost = float(input('Meal cost? '))
Right.
There are two things (sometimes more) we need to check:
- Did the user type a number?
- Is it too small or too big?
Let's do the first one first.
Did the user type a number?
Here's one way to do it.
- ...
- # Input
- # Get the cost of the meal.
- user_input = input('Meal cost? ')
- # Test whether input was numeric.
- try:
- meal_cost = float(user_input)
- except ValueError:
- print('Sorry, you must enter a number.')
- sys.exit()
- service_level = input('Service level (g=great, o=ok, s=sucked)? ')
- ...
try/except
has two blocks of code:
- try:
- Some code that might fail
- except Error type:
- Code to run if there's an error of type Error type
try/except
has other forms, but we'll leave it at this for now.

Ray
So it's, like, try to run this code. If something goes wrong, do this other thing.
Aye. In this case, try to convert a string into a float. If it doesn't work, show an error and exit.

Georgina
What's ValueError
in except ValueError:
?
There are many types of errors, like ConnectionError
if the network fails, ZeroDivisionError
if you try to divide by zero, etc. You list the errors you want to catch. You can list more than one in ()
if you like.

Ethan
But why ValueError
? How would we know that?
Good question. Use the console to check. In the console, run this:
- float('doggo')
What happened? Ray?

Ray
I got this:
- float('doggo')
- Traceback (most recent call last):
- Cell In[1], line 1
- float('doggo')
- ValueError: could not convert string to float: 'doggo'

Adela
Oh, OK. Make the error you want to prevent, and get the error type. ValueError
in this case.
Aye, that's it.

Georgina
A question. Here's the code again.
- ...
- # Input
- # Get the cost of the meal.
- user_input = input('Meal cost? ')
- # Test whether input was numeric.
- try:
- meal_cost = float(user_input)
- except ValueError:
- print('Sorry, you must enter a number.')
- sys.exit()
- # Get the service level.
- service_level = input('Service level (g=great, o=ok, s=sucked)? ')
- ...
Line 4 used to be:
- meal_cost = float(input('Meal cost? '))
One statement, with both float
and input
. Makes sense, put the number from the user into the variable meal_cost
.
The new version has:
- user_input = input('Meal cost? ')
Why the difference? Why user_input
and not meal_cost
.
Good noticing, Georgina!
input()
always returns a string, as we saw earlier. Putting it into float()
without checking it, like this would do...
- meal_cost = float(input('Meal cost? '))
could cause errors. So, we put whatever input()
returns into a variable called user_input
. Then we try to make a float
out of it, in a separate step.
What data type will user_input
be?

Ray
A string. Python makes user_input
whatever type is needed to contain the data from the right-hand side of the =
.
Indeed.
Here's the line again:
- user_input = input('Meal cost? ')
Why did I call the variable user_input
, and not meal_cost
, like I did before?

Ethan
Maybe because when the user types something, we can't be sure that it's a meal cost until we check it?
Aye! That's it. user_input
is a slightly better name.
Range check
Here's the meal cost validation code again:
- ...
- # Input
- # Get the cost of the meal.
- user_input = input('Meal cost? ')
- # Test whether input was numeric.
- try:
- meal_cost = float(user_input)
- except ValueError:
- print('Sorry, you must enter a number.')
- sys.exit()
- # Get the service level.
- service_level = input('Service level (g=great, o=ok, s=sucked)? ')
- ...
It checks whether the user's input was numeric.
What else could go wrong besides the user typing something that isn't a number?

Adela
Well, they might type, like 0, or a negative value. Or a huge number, like 1,000,000.

Georgina
Ooo! They could type nothing, and press Enter.
Yay! Two things. Nice work!
What happens when the user types nothing, and just presses Enter?

Georgina
Let me see... Ooo!
I got the error message. That's OK.
Yes, though you were right to bring it up. MT (that's "empty" - a geek thing) input is an edge case, something that's unlikely, but could happen. Don't forget to check edge cases.
What happens when the meal cost is negative?

Ethan
Ack! A negative tip. Negative meal cost doesn't make sense.
Oh, you can give it a zero meal cost, too. doesn't make sense, either.
Right.
We need to make sure meal cost isn't too small or too large. That's a range check.
Here's some code for it:
- ...
- # Input
- # Get the cost of the meal.
- user_input = input('Meal cost? ')
- # Test whether input was numeric.
- try:
- meal_cost = float(user_input)
- except ValueError:
- print('Sorry, you must enter a number.')
- sys.exit()
- # Test range.
- if meal_cost <= 0 or meal_cost > 1000000:
- print('Sorry, please enter a number more than zero, and less than 1,000,000.')
- sys.exit()
- # Get the service level.
- service_level = input('Service level (g=great, o=ok, s=sucked)? ')
- ...
That's our complete validation. Type check, then a range check.
Another pattern
Remember, a pattern is a way of doing a task people have found useful. There's a pattern for validating numeric fields.
One-time numeric input validation
- Get user input into a string variable.
- Try to convert the data to numeric. If it fails, show an error message, and stop the program.
- If it works, check whether the number is in the valid range (not too small, and not too large). If it fails, show an error message, and stop the program.
- If both checks pass, you have a number you can use for calculations.
Summary
- The previous lesson was about validating input strings. This lesson is about numbers.
- Use
try/except
to catch data type errors, that is, the user enters something that isn't a number. - Use an
if
to do a range check.
Exercise
Parade length
Write a program to help small towns estimate the length of parades, like their July 4th parade.
Parades have floats and marching bands. Each float needs 30 meters. Each band needs 50 meters.
Sample I/O:
- How many floats? 5
- How many bands? 2
- Total length: 250
Both inputs must be numbers, zero or more. Floats cannot be more than 10. Bands cannot be more than 5. Display appropriate error messages as shown below.
More I/O:
- How many floats? some
- Sorry, you must enter a number.
Even more I/O:
- How many floats? -3
- Sorry, please enter a number zero or more, and ten or less.
Yet more I/O:
- How many floats? 11
- Sorry, please enter a number zero or more, and ten or less.
Yes, more I/O:
- How many floats? 3
- How many bands? not sure
- Sorry, you must enter a number.
Upload a zip of your project folder. The usual coding standards apply.