Check the meal cost

Multiple choice

Some code:

  • x = input('Value for X? ')
  • y = input('Value for Y? ')
  • z = x * 2 + y
  • z += 10
  • if z < 30:
  •     message = 'Too low'
  • elif z < 80:
  •     message = 'OK'
  • else:
  •     message = 'Too large'
  • print(message)

What's the output if the user types 4.5 for X and 5.5 for Y?

Try to work it out in your head first. Then you can run the code.

Saving
A
Too low
B
OK
C
Too large
D

Error

Not graded. So why do it?

Multiple choice

What does this output if the user types 60 for the input?

  1. age = float(input('Age? '))
  2. if age >= 60:
  3.     is_old = 'yes'
  4. else:
  5.     is_old = 'no'
  6. print(is_old)
Saving
A

yes

B

no

C

Can't tell from the information given.

Not graded. So why do it?

Multiple choice

If you want to use this…

  • sys.exit()

… what should you add to your code?

Saving
A

import python.sys.exit

B

Nothing. It's a built-in function.

C

import sys

D

import python.sys

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".

Reflect

What happened?

If you were logged in as a student, the lesson would pause here, and you'd be asked to type in a response. If you want to try that out, ask for an account on this site.
Adela
Adela

I got an error: ValueError: could not convert string to float: 'doggo'

Reflect

Why did you get the error?

If you were logged in as a student, the lesson would pause here, and you'd be asked to type in a response. If you want to try that out, ask for an account on this site.
Adela
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
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.

Cute mammal

Georgina
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
Ethan

But why ValueError? How would we know that?

Good question. Use the console to check. In the console, run this:

  • float('doggo')
Reflect

What happened? Ray?

If you were logged in as a student, the lesson would pause here, and you'd be asked to type in a response. If you want to try that out, ask for an account on this site.
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
Adela

Oh, OK. Make the error you want to prevent, and get the error type. ValueError in this case.

Aye, that's it.

Georgina
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.

Reflect

What data type will user_input be?

If you were logged in as a student, the lesson would pause here, and you'd be asked to type in a response. If you want to try that out, ask for an account on this site.
Ray
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? ')
Reflect

Why did I call the variable user_input, and not meal_cost, like I did before?

If you were logged in as a student, the lesson would pause here, and you'd be asked to type in a response. If you want to try that out, ask for an account on this site.
Ethan
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.

Reflect

What else could go wrong besides the user typing something that isn't a number?

If you were logged in as a student, the lesson would pause here, and you'd be asked to type in a response. If you want to try that out, ask for an account on this site.
Adela
Adela

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

Georgina
Georgina

Ooo! They could type nothing, and press Enter.

Yay! Two things. Nice work!

Reflect

What happens when the user types nothing, and just presses Enter?

If you were logged in as a student, the lesson would pause here, and you'd be asked to type in a response. If you want to try that out, ask for an account on this site.
Georgina
Georgina

Let me see... Ooo!

MT input

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.

Reflect

What happens when the meal cost is negative?

If you were logged in as a student, the lesson would pause here, and you'd be asked to type in a response. If you want to try that out, ask for an account on this site.
Ethan
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.

Pattern

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

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.