Keep asking: strings

Emily's doggos
Emily's doggos

Multiple choice

What will this output, if the user types cat for the input?

  1. pet_type = input('Pet type? ');
  2. if (pet_type == 'dog' or pet_type == 'cat' or pet_type == 'goat'):
  3.     message = 'Favored pet'
  4. else:
  5.     message = 'Not a favored pet'
  6. print(message)
Saving
A
Favored pet
B
Not a favored pet
C

Can't tell from the information given.

Not graded. So why do it?

Multiple choice

What will this output, if the user types Corn dog for the input?

  1. dish = input('Dish? ')
  2. is_yummy = 'yes'
  3. if (dish == 'corn dog'):
  4.     is_yummy = 'no'
  5. print (is_yummy)
Saving
A
yes
B
no
C

Can't tell from the information given.

Not graded. So why do it?

Don't stop asking

So far, when the user makes an error, the program shows a message and stops. This can be annoying, especially if there's lots of input, and the user types just one thing wrong. Argh!

Another approach is to keep asking until the user enters something valid. Here's some console interaction:

  • Meal cost? 60
  • Service level (g=great, o=ok, s=sucked)? asdf
  • Please enter G, O, or S.
  • Service level (g=great, o=ok, s=sucked)? f
  • Please enter G, O, or S.
  • Service level (g=great, o=ok, s=sucked)? asdf
  • Please enter G, O, or S.
  • Service level (g=great, o=ok, s=sucked)? o
  • -----------------
  • Meal cost: 60.0
  • Tip rate: 15 %
  • Tip: 9.0
  • Total: 69.0
Reflect

Why is this better than simply stopping the program on user input 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.
Georgina
Georgina

People make errors all the time. This approach doesn't punish someone for a simple typing mistake.

True dat.

The boss
The boss

Baby steps

Let's start with something simple you've seen before, and build up. Let's write a program asking users for the best pet. It keep asking until they type the right pet, which is, of course, dogs.

Here's sample I/O:

  • What is the best pet? cat
  • What is the best pet? llama
  • What is the best pet? lizard
  • What is the best pet? dog
  • Best pet: dog

The program keeps asking until you give it the right answer.

Here's one way to write the program:

  1. best_pet = ''
  2. while best_pet != 'dog':
  3.     best_pet = input('What is the best pet? ')
  4. print('Best pet: ' + best_pet)

Line 1 creates an MT string variable.

Line 2 starts a while loop. It has the form:

  • while condition:
  •     action

condition is either true or false, just like an if uses. Python will keep doing action while condition is true.

Reflect

What's the != in line 2 mean?

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

Not equal to.

Aye.

Clean up user input

Georgina
Georgina

What if they type "Dog" instead of "dog"?

Oh, good point. Or they might type DOG or doG or add some spaces.

Reflect

Fill this in:

When we __________________ a variable, we convert it to a common format that's easier to test.

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

Normalize, right?

Aye.

Pattern

Normalize a string

Simplify string validity checks by converting user input to a standard form.

When you start a new project, you can use the pattern catalog to remind yourself of useful chunks of code.

Here's the code with comments. Normalization is on line 8, removing extra spaces, and making everything lowercase.

  1. # Initialize best_pet to MT.
  2. best_pet = ''
  3. # While best_pet does not equal dog...
  4. while best_pet != 'dog':
  5.     # Ask the user what the best pet is.
  6.     best_pet = input('What is the best pet? ')
  7.     # Normalize
  8.     best_pet = best_pet.strip().lower()
  9. # Output the best pet.
  10. print('Best pet: ' + best_pet)

Keep asking

Back to the tip program. We keep asking for the service level until the user enters a valid value.

  • Meal cost? 60
  • Service level (g=great, o=ok, s=sucked)? asdf
  • Please enter G, O, or S.
  • Service level (g=great, o=ok, s=sucked)? f
  • Please enter G, O, or S.
  • Service level (g=great, o=ok, s=sucked)? asdf
  • Please enter G, O, or S.
  • Service level (g=great, o=ok, s=sucked)? o
  • -----------------
  • Meal cost: 60.0
  • Tip rate: 15 %
  • Tip: 9.0
  • Total: 69.0

You learned about while:

  • while condition:
  •     action

For the validating the service level, what can you tell me about condition? What are we testing?

Georgina
Georgina

We want to keep looping until we get a valid service level, right?

So, maybe, action is something like "while service level is not valid."

Right! This seems backwards at first, but it isn't. If the service level is valid, we don't need to ask again. If it's not valid, we need to ask again.

  • while service level is not valid:
  •     action
Reflect

What will action do, in a few words?

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

Could be, like, "ask user to input the service level?" And normalize to make testing easier.

Makes sense.

  • while input is not valid:
  •     ask user to input the service level
Adela
Adela

Yeah, but how do we show the error message? We want I/O like...

  • Service level (g=great, o=ok, s=sucked)? asdf
  • Please enter G, O, or S.
Ethan
Ethan

We used an if in a previous lesson:

  • # Ask user to type in the service level.
  • service_level = input('Service level (g=great, o=ok, s=sucked)? ')
  • # Normalize service_level input
  • service_level = service_level.strip().lower()
  • # Validate service_level input
  • if service_level != 'g' and service_level != 'o' and service_level != 's':
  •     print('Please enter G, O, or S.')
  •     sys.exit()

So something like:

  • while Input is not valid:
  •     Ask user to input the service level
  •     Normalize input
  •     if input not valid:
  •         Show error message

Good!

Georgina
Georgina

Hey, good work, Ethan!

Can you put some real Python in there?

Georgina
Georgina

I'll try. We had this:

  • # Ask user to type in the service level.
  • service_level = input('Service level (g=great, o=ok, s=sucked)? ')
  • # Normalize service_level input
  • service_level = service_level.strip().lower()
  • # Validate service_level input
  • if service_level != 'g' and service_level != 'o' and service_level != 's':
  •     print('Please enter G, O, or S.')
  •     sys.exit()

I'll copy one bit in.

  • while Input is not valid:
  •     Ask user to input the service level
  •     Normalize input
  •     if input not valid:
  •         # Show error message
  •         print('Please enter G, O, or S.')
Ray
Ray

Cool! You left that bit of English in there as a comment.

  • Show error message

Became:

  • # Show error message
  • print('Please enter G, O, or S.')

I like that!

Aye, that's a good idea.

  • while Input is not valid:
  •     Ask user to input the service level
  •     Normalize input
  •     if input not valid:
  •         # Show error message
  •         print('Please enter G, O, or S.')

What's some other Python we can add?

Georgina
Georgina

These things:

  •     Ask user to input the service level
  •     Normalize input

We did those in the nonlooping program. So:

  • while Input is not valid:
  •     # Ask user to input the service level
  •     service_level = input('Service level (g=great, o=ok, s=sucked)? ')
  •     # Normalize input
  •     service_level = service_level.strip().lower()
  •     if input not valid:
  •         # Show error message
  •         print('Please enter G, O, or S.')

Cool.

Ethan
Ethan

Wait! We had the test thing in other program too! Here's the old code:

  • # Ask user to type in the service level.
  • service_level = input('Service level (g=great, o=ok, s=sucked)? ')
  • # Normalize service_level input
  • service_level = service_level.strip().lower()
  • # Validate service_level input
  • if service_level != 'g' and service_level != 'o' and service_level != 's':
  •     print('Please enter G, O, or S.')
  •     sys.exit()

We could copy the thing in the if.

  1. # Is the service level valid?
  2. while service_level != 'g' and service_level != 'o' and service_level != 's':
  3.     # Don't have a valid service level yet
  4.     # Ask user to input the service level
  5.     service_level = input('Service level (g=great, o=ok, s=sucked)? ')
  6.     # Normalize input
  7.     service_level = service_level.strip().lower()
  8.     # Is the service level valid?
  9.     if service_level != 'g' and service_level != 'o' and service_level != 's:
  10.         # Show error message
  11.         print('Please enter G, O, or S.')

Nice!

Ray
Ray

We had sys.exit() in the other program. Shouldn't it be in this one, too?

Georgina
Georgina

No. In the other program, we wanted to stop the program if there was an error.

Here, we don't want to stop. We want to ask the user again.

Ray
Ray

But, where do we give the question again? It's only in the code once, on line 5.

Georgina
Georgina

The question is only in the code once, but that one line is run again and again while the loop is active.

After getting to the end of the loop, Python goes back to the while. If the test is still true, it runs the code in the loop again. So the user gets the question again.

Looping

Right! The code doesn't run from top to bottom. It starts that way, then it goes up, around and around.

Something's missing

If you try this code, it will crash. Something's missing. What is it? Hint: here's the code from the best pet program:

  • best_pet = ''
  • while best_pet != 'dog':
  •     best_pet = input('What is the best pet? ')
  • print('Best pet: ' + best_pet)
Reflect

What's missing from the service level validation code?

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, right. You have to make sure Python creates a variable before you try to use it. The line...

  • while service_level != 'g' and service_level != 'o' and service_level != 's':

... won't work unless the variable service_level exists. So, add service_level = '' to the top.

Right!

  1. # Initialize service_level
  2. service_level = ''
  3. # Is the service level valid?
  4. while service_level != 'g' and service_level != 'o' and service_level != 's':
  5.     # Don't have a valid service level yet
  6.     # Ask user to input the service level
  7.     service_level = input('Service level (g=great, o=ok, s=sucked)? ')
  8.     # Normalize input
  9.     service_level = service_level.strip().lower()
  10.     # Is the service level valid?
  11.     if service_level != 'g' and service_level != 'o' and service_level != 's:
  12.         # Show error message
  13.         print('Please enter G, O, or S.')

Pattern

Let's make a new pattern from this code. Then we can use the code for any string input from the console.

Pattern

String validation loop

Use a while loop to keep asking the user for input, until they type a valid value.

Counting attempts

Here's the best pet code again.

  1. best_pet = ''
  2. while best_pet != 'dog':
  3.     best_pet = input('What is the best pet? ')
  4. print('Best pet:' + best_pet)

Suppose you wanted to count the number of times the user was asked the question? I/O might be:

  • What is the best pet? cat
  • What is the best pet? bird
  • What is the best pet? horse
  • What is the best pet? dog
  • Best pet: dog
  • Number of times asked: 4

How do we do it?

Ray
Ray

Er... how do we start?

Ethan
Ethan

How about a small step we know will have to be done? We'll need to output the number of times the user was asked.

  1. best_pet = ''
  2. while best_pet != 'dog':
  3.     best_pet = input('What is the best pet? ')
  4. print('Best pet: ' + best_pet)
  5. print('Number of times asked:' + str(counter))
Ethan
Ethan

I named the variable counter, but it could be something else.

Adela
Adela

We'll need to add one to counter, in the loop. So each time the loop runs, the count goes up.

Let's add that to the code.

  1. best_pet = ''
  2. while best_pet != 'dog':
  3.     best_pet = input('What is the best pet? ')
  4.     counter += 1
  5. print('Best pet: ' + best_pet)
  6. print('Number of times asked: ' + counter)

Nice!

Reflect

What's missing?

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

I tried it, and got an error in line 4: counter += 1. The program tries to add 1 to counter, but counter doesn't exist.

  1. best_pet = ''
  2. counter = 0
  3. while best_pet != 'dog':
  4.     best_pet = input('What is the best pet? ')
  5.     counter += 1
  6. print('Best pet: ' + best_pet)
  7. print('Number of times asked: ' + str(counter))
Reflect

In line 2, does counter have to be set to zero? Would another value, say 17, work?

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

No. It needs to start at zero, otherwise we'll output too many at the end.

Indeed.

Summary

  • Keep asking the user for input until they enter something valid.
  • A while loop keeps running a code block while an expression is true.

Exercises

Exercise

Input dog size

Write a program asking the user what size dog they want: small, medium, or large. Here's some I/O:

  • What size dog do you want (small, medium, large)? tiny
  • What size dog do you want (small, medium, large)? enormous!
  • What size dog do you want (small, medium, large)? SMall
  • OK, let's get you a small dog.

The program keeps asking until the user types a valid value. Let them use whatever case they want, and add extra spaces at the beginning and end.

Note that in the last case, the user types SMall, but the output says small. Your program should do that.

Upload a zip of your project folder. The usual coding standards apply.

Exercise

Ulma's Ungulates

Ulma rents out animals for people to ride. Write a program to compute the price of renting an animal. You can rent either a llama or a camel.

The price depends on:

  • Which animal you choose.
  • Whether you want to rent a saddle as well.

Ask the user to choose an animal, and saddle choice. Validate all the input, using the following rules:

  • The animal input must be either L or C. Case doesn't matter. There can be extra spaces, and the program will still work.
  • Saddle is either Y or N. Case doesn't matter. There can be extra spaces, and the program will still work.

Keep asking until the user enters valid data. (See I/O below.)

When the data is OK, output the full name of the animal (camel or llama), a warning, and the price.

The warning is "Spits" for llamas, and "Stinks" for camels.

  • Llama rent for $80.
  • Camels rent for $95.
  • Saddles rent for $30.

Here's some I/O.

  • Animal (C=Camel, L=Llama)? cat
  • Please enter C or L.
  • Animal (C=Camel, L=Llama)? l
  • Saddle (Y=yes, N=no)? Maybe
  • Please enter Y or N.
  • Saddle (Y=yes, N=no)? N
  •  
  • Ulma's Ungulates
  • - - - - - - - -
  •  
  • Animal: Llama
  •   Warning: Spits!
  • Saddle: No
  • - - - - - - - -
  • Price $: 80

Some more I/O:

  • Animal (C=Camel, L=Llama)? humpy one
  • Please enter C or L.
  • Animal (C=Camel, L=Llama)? c
  • Saddle (Y=yes, N=no)? y
  •  
  • Ulma's Ungulates
  • - - - - - - - -
  •  
  • Animal: Camel
  •   Warning: Stinks!
  • Saddle: Yes
  • - - - - - - - -
  • Price: $ 125

Upload a zip of your project folder. The usual coding standards apply.