Not graded. So why do it?
Not graded. So why do it?
In the last lesson, you learned how to keep asking the user for a text input until they gave a valid value. Now, let's ask them to type in a float, and keep asking until we get a good one.
I/O
Here's a sample of the I/O we want.
- Meal cost? don't know
- Sorry, you must enter a number.
- Meal cost? sixty dollars
- Sorry, you must enter a number.
- Meal cost? -10
- Sorry, please enter a number more than zero, and less than 1,000,000.
- Meal cost? 1234567
- Sorry, please enter a number more than zero, and less than 1,000,000.
- Meal cost? 60
- Service level (g=great, o=ok, s=sucked)? o
- -----------------
- Meal cost: 60.0
- Tip rate: 15 %
- Tip: 9.0
- Total: 69.0
There are two types of errors users can make:
- Entering nonnumeric data. Lines 2 and 4 show what error message we want.
- Entering a number that's out of range, that is, too small or too big. See lines 6 and 8 show you the error message.
Asking one time
Here's the code we used earlier to validate that user input is numeric. The nonlooping version.
- # 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()
Line 2 gets data from the user.
What data type will user_input
be?

Georgina
It'll be a string. input()
always returns a string.
Right.
Lines 5...
- meal_cost = float(user_input)
...tries to convert what the user typed (which is in user_input
) into a float. It might fail, so we wrap line 5 in a try/except block.
- try:
- meal_cost = float(user_input)
- except ValueError:
- print('Sorry, you must enter a number.')
- sys.exit()
If there's a ValueError
- which is what we'll get if user_input
isn't numeric - the code in the except block will run. It will output a message, and stop the program.
Checking for integers
Here's the code again:
- try:
- meal_cost = float(user_input)
- except ValueError:
- print('Sorry, you must enter a number.')
- sys.exit()
This checks whether the user entered a float value, like 22.44. In an earlier lesson, we talked about integers. Integers are whole numbers. So 32 is an integer, but 3.2 is not.
This is from the earlier lesson:
Integers are primarily used for counting things. For example, if you're counting people, you have 23 people or 24 people, not 23.4 people. I wouldn't want 0.4 of a person at a party. Eeewwww.
Say we wanted users to tell us how many people were invited to a party. Here's some code:
- try:
- people = int(user_input)
- except ValueError:
- print('Sorry, you must enter a whole number.')
- sys.exit()
Only one thing has changed: float
has become int
. Python has more data types, and strings can be converted into most of them. We'll only about the ones we need for this course.
Going loopy
We'll wrap the code in a while
loop, just as we did for the string validation loop.
With string validation, there was one kind of error: not one of the valid options. Now, there are two kinds of errors people can make: nonnumeric and out-of-range.
However, in general, there can be any number of kinds of errors. For example, suppose you have an order form on a website. Customers enter the number of items they want to buy.
Vegemite is yummy! Well, to me.
Possible errors include:
- User types nonnumeric data.
- User types an out-of-range value, like -3 or 191,299,133.
- User types a number, but we don't have that many in stock.

Ethan
Isn't the last one a range error?
Kinda, but the top value varies depending on inventory. Today, you can order up to, say, 58. Tomorrow, up to 42, because we sold some.
Business policies might mean we add other errors. For example, maybe new customers can only order up to 5 jars. So trying to order 6 might or might not be an error, depending on who's placing the order.
The point is, there can be any number of kinds of errors.
It'd be nice if we had a way to write a validation loop we could easily add and remove error tests.
Not graded. So why do it?
A better way
Here's a way to do it that works well. Let's start with a pseudocode version.
We need a variable to keep track of whether the input is OK or not. Let's call it is_input_ok
.
(Remember, this is not real code yet.)
- is_input_ok = No
- While is_input_ok is No:
- is_input_ok = Yes
- Get input
- if data is bad for some reason:
- is_input_ok = No
- if data is bad for some other reason:
- is_input_ok = No
- As many tests as we want
Each time through the loop, we start by assuming the input is OK (line 3). Then we get the input from the user (line 4). Then we have a buncha tests. In each one, if there's a problem, we remember the data is bad.
By the time we get to the end of the loop's code block (after line 9 is done), we have only two possibilities for is_input_ok
:
- If none of the tests shows the data is bad,
is_input_ok
will still be Yes. It's set to Yes in line 3, and never changes to No. - If any of the tests show there's a problem,
is_input_ok
is No.
Then loop back to the while (line 2). If is_input_ok is No, run the loop again. Keep running it while is_input_ok is No.
Why does line 1 set is_input_ok to No?

Adela
So the while loop runs at least once.
Aye, that's it.
Let's look at a more Pythony version.
- is_input_ok = False
- while not is_input_ok:
- is_input_ok = True
- Get some input
- if some test:
- Show error message
- is_input_ok = False
- if is_input_ok:
- if some other test:
- Show error message
- is_input_ok = False
- if is_input_ok:
- if yet another test:
- Show error message
- is_input_ok = False
- if is_input_ok:
- if yet yet another test:
- Show error message
- is_input_ok = False
- As many tests as we like
is_input_ok
is a boolean variable. We have strings, floats, integers, and now we have another data type: boolean. Boolean variables can only be True
or False
. They're mainly used to remember whether something happened or not, like whether a program found an error in user input.
Let's break it down. Line 1...
- is_input_ok = False
... creates the variable and sets it to False
. False
is a boolean constant. There are only two boolean constants, since a boolean can only be true or false. Python wants the first letter of true and false to be uppercase. I don't know why.
Line 2...
- while not is_input_ok:
... keeps running its code block until is_input_ok
is True
, that is, is not False
. In English, it would be: while the input isn't OK.
We're about to do the first pass through this code:
- is_input_ok = False
- while not is_input_ok:
- is_input_ok = True
- Get some input
- if some test:
- Show error message
- is_input_ok = False
- if is_input_ok:
- if some other test:
- Show error message
- is_input_ok = False
- if is_input_ok:
- if yet another test:
- Show error message
- is_input_ok = False
- if is_input_ok:
- if yet yet another test:
- Show error message
- is_input_ok = False
- As many tests as we like
Start by assuming the input we're about to get is OK (line 3). Get the input (line 4). Then a buncha tests, as many as we like. If any fail, do is_input_ok = False
.
When the code block is done, go back up to the while
(line 2):
- while not is_input_ok:
If is_input_ok
is False
, ask the user again.

Georgina
The last lesson's loop, the string validation one. Could we have written it with a boolean?
Aye. I like to explain things in small steps, though. There was enough new stuff in that lesson without adding booleans as well.
Here's the code from then, and a new version.
|
|

Georgina
This stuff is so cool!

Ethan
Yeah, it is. So logical. Lotsa deets, though.
Indeed. Using patterns, comments, and meaningful variable names help you with the deets. Managing your brain's load is important for programmers. The real name for "brain load" is cognitive load.
The next part of the course is about functions, another way of reducing cognitive load.
The flag pattern
is_input_ok
is a flag variable. The word "flag" is used in various ways in programming. To keep it simple, we'll use it one way.
Use a variable as a flag. Set the flag if any of a number of things happens. After, check the flag to see if any of those things happened.
When you start a new project, you can use the pattern catalog to remind yourself of useful chunks of code.

Georgina
I have an idea, but don't know whether it's right.
You've told us to make programs easy to think about. They have fewer bugs, and are easier to change.
Right. That's part of software cost and quality control.

Georgina
So, look at this flaggy pseudocode:
- is_data_ok = False
- while not is_data_ok:
- is_data_ok= True
- Get some data
- if something is wrong with the data
- Do something
- is_data_ok = False
- if is_data_ok:
- if something else is wrong with the data
- Do something else
- is_data_ok = False
- if is_data_ok:
- if something else is wrong with the data
- Do something else
- is_data_ok = False
- if is_data_ok:
- if something else is wrong with the data
- Do something else
- is_data_ok = False
- ...
To add another test, I don't need to think about the entire program. As long as I use is_data_ok
correctly, I can just work on the test. Easier on my brain, so less chance of a bug.
Aye! That's exactly right! Breaking code into independent chunks makes life easier. The flag pattern helps with that.
Back to meal cost
OK, let's use this stuff to make a meal cost validation loop for the tip program. Remember, the pattern is:
- is_input_ok = No
- While is_input_ok is No:
- is_input_ok = Yes
- Get input
- if data is bad for some reason:
- is_input_ok = No
- if data is bad for some other reason:
- is_input_ok = No
- As many tests as we want
Let's change it to Python.
Who wants to start?

Ethan
I can. Here are the first few lines.
Pseudocode | Python |
---|---|
|
|
Good! Now, we want to add two tests.
What two things do we need to check?

Ray
Whether the input is a number.
Whether the number is too small or too big.
Right.
I'll add some comments to remind us.
Pseudocode | Python |
---|---|
|
|

Adela
I can do the first check.
What's the code for the numeric test?

Adela
I'd do it like this...
- # Is the input numeric?
- try:
- meal_cost = float(user_input)
- except ValueError:
- print('Sorry, you must enter a number.')
- is_input_ok = False
Note
Adela could think just about the numeric test code, because of the regular structure of the pattern. Easier than trying to keep the entire program in her mind all at once. Easy is good.
Who wants to do the range check?
Write the code for the range check.

Georgina
Ooo! Me!
- # Test range.
- if is_input_ok:
- if meal_cost <= 0 or meal_cost > 1000000:
- print('Sorry, please enter a number more than zero, and less than 1,000,000.')
- is_input_ok = False
Good!
Note
Georgina only has to know about is_input_ok
and meal_cost
. The rest of the program doesn't matter while she writes this code fragment. Easier that way. Easy is good.
Let's put it all together.
Pseudocode | Python |
---|---|
|
|
Done!
Patternism
A new pattern.
Loop while
a data-OK flag is false:
Set a data-OK flag to true.
Check whether the data is numeric. If not, set the data-OK flag to false.
If the data-OK flag is still true, do a range test. If the data is out of range, set the data-OK flag to false.
Repeat for as many tests as you need.
Summary
- A
while
loop can keep asking the user for input until it gets something valid. - There are at least two kinds of errors people can make: nonnumeric and out-of-range.
- Use a variable as a flag. Set the flag if any of a number of things happens. After, check the flag to see if any of those things happened.
Exercise
Economic order quantity
Your company buys Stuffs from wholesalers, and sells it retail. Work out the optimal order quantity. That is, how many Stuffs you should order each time to minimize cost.
Here's some I/O:
- Sales? 5000
- Ordering cost? 90
- Carrying cost? 6
- Are there Smurfs (Y/N)? n
- EoQ: 387
More:
- Sales? 5000
- Ordering cost? 90
- Carrying cost? 6
- Are there Smurfs (Y/N)? y
- EoQ: 388
One more:
- Sales? some
- Sorry, you must enter a whole number.
- Sales? 5000
- Ordering cost? -3
- Sorry, ordering cost cannot be less than zero.
- Ordering cost? 90
- Carrying cost? something
- Sorry, you must enter a number.
- Carrying cost? 6
- Are there Smurfs (Y/N)? Don't know
- Please enter Y or N.
- Are there Smurfs (Y/N)? y
- EoQ: 388
Validation rules:
- Sales (units per year). Must be an integer that is one or greater.
- Ordering cost ($). The cost of placing one order. Must be a float that is not negative.
- Carrying cost ($/unit/year). Cost of carrying a unit per year. Storage, insurance, like that. Must be a float that is more than zero.
- Whether Smurfs are present. After trimming spaces, should be Y or N, though case doesn't matter.
When the data is OK, compute the optimal order quantity, and show it. The formula from Wikipedia is:
Where:
If there are Smurfs, add one to the order quantity. They always steal just one.
Upload a zip of your project folder. The usual coding standards apply.