Repeated test

We've got a long validation test, and it's in the code twice:

  • while service_level != 'g' and service_level != 'o' \
  •     and service_level != 's' and service_level != 'q':
  •     ...
  •     if service_level != 'g' and service_level != 'o' \
  •         and service_level != 's' and service_level != 'q':

It works, but from a programming cost point of view, having the test just once would be better.

Reflect

Why? Hint: it's not about writing the code the first time.

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, OK, thanks for the hint.

I think it's about making changes. If we wanted to add a new option, like h for help or something, we'd have two tests to change.

Ray
Ray

What's wrong with that? Copy-and-paste is easy.

Adela
Adela

Yes, but the more times the test is in the code, the more likely we'll mess up the program when adding a new option. That means more time debugging.

Adela's right. Most programmers spend more time updating existing programs, rather than writing new programs. Makes sense. If you've got a program that almost does the job, it's a lot cheaper to change the existing program, than make a new one.

It's not a big deal with just one repeated test, but real programs can have dozens of them. The more complicated the tests in ifs and whiles, the easier it is to mess things up.

Multiple choice

Every variable has a data type, like string or float. If two variables have different data types, what does that mean?

Saving
A

The variables must have different values. So if x = 3.14 and y = 2.71, x and y are different types.

B

The variables store different kinds of data.

C

Variables are more likely to marry other variables of the same type, than variables of a different type.

Not graded. So why do it?

Here's another way to do things.

Let's add a new variable. Its job is to know whether the input is valid or not:

  • is_input_ok = (service_level == 'g' or service_level == 'o' \
  •         or service_level == 's' or service_level == 'q')

Then, instead of repeating the test, we use is_input_ok.

Ethan
Ethan

I'm confused.

OK. Type this in the console:

  • 5 + 2.5

Python evaluates (works out) the expression, and gives you back 7.5.

Another:

  • 2 < 5

Python evaluates the expression, and gives you back...

Reflect

What does Python give you?

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

It gave True, because 2 is less than 5.

Right.

Right. So just as 5 + 2.5 is an expression returning a value, so 2 < 5 is an expression returning a value.

Multiple choice

What's a float in Python?

Saving
A

A whole number with no decimal part, like 2 and 44, but not 3.14.

B

A number with a decimal part, like 3.14 or 2.71. 5.0 is not a float.

C

A number that might have a decimal part, but doesn't have to. 3.14, 2.71, and 5.0 are all floats.

Not graded. So why do it?

We can check out the data type of a value with the type() function. In the console:

  • type(5 + 2.5)
Reflect

What do you get?

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

It said float. The expression 5 + 2.5 return a float.

OK. Now try:

  • type(2 < 5)
Reflect

What did you get?

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

It says bool.

Aye. Boolean is a new data type. We know about these types so far:

  • String: characters you can type, like "The cannibal passed his brother in the woods."
  • Float: a number that might have a decimal part, like 8.32.
  • Boolean: either true or false.

Let's put in some variables. Assignment (=) takes a value on the right, and puts it into the variable on the left:

  • variable = value

Another example, using the same two expressions.

  • x = 5 + 2.5
  • y = 2 < 5
  • print('x is', x)
  • print('y is', y)
Reflect

Paste those four lines into the console. What's the output?

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

I got:

  • x is 7.5
  • y is True

Indeed. And when I check out ye olde Variable Explorer:

Types

The type column shows the types.

One more:

  1. cows = 4
  2. pigs = float(input('How many pigs? '))
  3. is_more_pigs = (pigs > cows)
  4. print('More pigs than cows? ', is_more_pigs)
Reflect

(Don't run the code yet.) What's the output going to be, if the user types 3 for the number of pigs?

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

If they type 3 for pigs, then line 3 says is_more_pigs is false, since 3 is not more than 4. So I should get More pigs than cows? False

Indeed! Try it to check.

Booleans have many uses. One is to break apart ifs. These are equivalent:

  • if sheep > goats and cattle_dogs > (sheep/20) and shepherds > (sheep/50):
  •  
  • or
  •  
  • is_got_enough = sheep > goats and cattle_dogs > (sheep/20) and shepherds > (sheep/50)
  • if is_got_enough:

Notice started the variable name with is_. That's a common standard, using is_, was_, or something else that looks like it's a yes/no thing.

This is what the code is right now:

  • service_level = ''
  • while service_level != 'g' and service_level != 'o' \
  •     and service_level != 's' and service_level != 'q':
  •     service_level = input(
  •         'Service level (g=great, o=ok, s=sucked, q=quit)? '
  •     )
  •     # Normalize service level
  •     service_level = service_level.strip().lower()
  •     # Validate service level
  •     if service_level != 'g' and service_level != 'o' \
  •         and service_level != 's' and service_level != 'q':
  •         print('Please enter G, O, S, or Q.')

Here's another version, using a boolean.

  1. is_input_ok = False
  2. while not is_input_ok:
  3.     service_level = input('Service level (g=great, o=ok, s=sucked, q=quit)? ')
  4.     # Normalize service level
  5.     service_level = service_level.strip().lower()
  6.     # Validate service level
  7.     is_input_ok = service_level == 'g' or service_level == 'o' \
  8.         or service_level == 's' or service_level == 'q'
  9.     if not is_input_ok:
  10.         print('Please enter G, O, or S.')

Georgina, could you run through the code?

Georgina
Georgina

Sure. The first line initializes the boolean. It's set to false, so line 2 will run the code in the loop.

Lines 3 and 5 get and normalize user input.

Line 7 sets the boolean to true or false, depending on whether the user entered valid input.

Hmm. Could I use parens around the expression? To better see what's going on.

Sure, and that means you don't need to use the \ line continuation thing, either.

  • is_input_ok = (service_level == 'g' or service_level == 'o'
  •         or service_level == 's' or service_level == 'q')

Actually, if you wanted, you could lay it out like this:

  •     is_input_ok = (
  •            service_level == 'g'
  •         or service_level == 'o'
  •         or service_level == 's'
  •         or service_level == 'q'
  •     )

That's how I would do it in a real program. Makes it easier to understand what's going on.

Let's change the code to:

  1. is_input_ok = False
  2. while not is_input_ok:
  3.     service_level = input('Service level (g=great, o=ok, s=sucked, q=quit)? ')
  4.     # Normalize service level
  5.     service_level = service_level.strip().lower()
  6.     # Validate service level
  7.     is_input_ok = (
  8.            service_level == 'g'
  9.         or service_level == 'o'
  10.         or service_level == 's'
  11.         or service_level == 'q'
  12.     )
  13.     if not is_input_ok:
  14.         print('Please enter G, O, or S.')
Georgina
Georgina

Cool! I like it.

After lines 7 to 12 run, is_input_ok is either true or false. If the input is hot OK, show an error. Then go back to the top of the loopn

Oh! I just noticed. You flipped the logical expression (a logical expression is either true or false) around. Before, we had:

  •     if service_level != 'g' and service_level != 'o' \
  •         and service_level != 's' and service_level != 'q':
  •         print('Please enter G, O, S, or Q.')

That tests whether the input is invalid. If not g and not o and not s and not q, that's invalid.

In the new code, you test whether the input is valid.

is_input_ok = ( service_level == 'g' or service_level == 'o' or service_level == 's' or service_level == 'q' )

If service_level is g, o, s, or q, then is_input_ok is true. The input's OK.

I like that better.

Adela
Adela

Yes, I do too. Easier to understand.

Booleans make life easier.

  • The name of is_input_ok makes it clear what the variable means.
  • We only have one copy of the logical expression, making changes easier.
  • The code is easier to understand. That means easier to change and debug.

Win win!

Summary

Exercises

Add loop for meal cost.