Making Decisions in our programs¶
Read time: 30 minutes (7531 words)
See also
Reading:
Text: Chapter 4
Programming involves lots of decisions and loops, we need to examine how Python handles these common concepts!
Decision making¶
Decisions are at the heart of most interesting programs. You examine data and decide what to do based on what your program finds.
Basics of making decisions¶
Computers can only ask simple questions. Oddly enough, those questions must be
in the form of simple True
or False
questions. Nothing else is allowed.
Most of our more interesting statement types use the answers to such questions
to decide how to operate.
The foundation of all question asking in computers is a simple comparison
of two data items. The two items must be of the same type to make any sense!
With that done, many of the comparisons involve numerical comparisons. For
example, if we subtract item one from item two what result might we get?
negative |
item one was larger than item two |
zero |
item one was identical to item two |
positive |
item one was smaller than item two |
So, if we can check these simple conditions, we can form basic logical
comparisons that will result in a True
or False
answer. We have come up
with a set of standard comparisons, and Python has them as well!
Comparison operators¶
Here are the basic operators, they should be easy enough to figure out. (They are similar to those used in many programming languages).
> |
greater than |
>= |
greater or equal |
< |
less than |
<= |
less or equal |
== |
equal to |
!= |
not equal to |
not |
invert the comparison |
We can also use another operator to reverse the boolean value of these expressions:
test = 5 > 10 # results in False
test = not (5 > 10) # results in True
What can be checked?¶
It should be obvious that we can compare numbers with these operators. Can we check anything else?
vara = 'Good'
varb = 'Bad'
if vara == 'Good':
print('We have a good string')
if varb != 'Good':
print('We have a bad string')
Strings can be compared with the relational operators, and the result is based on alphabetic order:
>>> vara = 'junk'
>>> varb = 'liver'
>>> vara < varb
True
The if-then-else statement¶
There are places where these questions are asked inside other statements. The
most common is probably the if-then`else
statement:
if question:
print "answer was True"
else:
print "Answer was False"
Warning
In this statement, indenting is how we show what code we want to execute in each part. Python is especially picky about indenting. This is a good habit to acquire for other languages where the indenting is not required, but encourages!
In the spot where question is found, we can either place an expression that
evaluates to True
or False
, or place a variable that has a boolean
value in it!
>>> vara = 5
>>> varb = 10
>>> vara > varb
False
>>> varb > vara
True
>>> vara == varb
False
>>> answer = vara > varb
>>> if answer:
... print "I got a True"
... else:
... print "I got a False"
...
I got a False
>>> if vara > varb:
... print "I got a True"
... else:
... print "I got a False"
...
I got a False
>>>
This allows us to set up questions as we wish. Which one do you choose? Which ever one that makes you program the clearest!
The else part is optional¶
In many cases, you only need to do something if the value of your expression is true. In this case the else part can be left off:
if income > bills:
print('Put something in savings!')
Combining logical values¶
There are a few more operators available that let you ask more complex
questions. These involve the logical operations. The most common are and
and or
which work as you might suspect based on your knowledge of English.
If one thing is true and another thing is false what can you say about both of
them? The answer can be figured out using a simple scheme known as a truth
table
.
Truth tables¶
A truth table
lists all possible inputs on for the variables, and show the
result of the operator.
Here are the rules for the and
operator:
A |
B |
A and B |
False |
False |
False |
True |
False |
False |
False |
True |
False |
True |
True |
True |
And, here are the rules for the or
operator:
A |
B |
A or B |
False |
False |
False |
True |
False |
True |
False |
True |
True |
True |
True |
True |
So we see that A and B
is only True if both A and B are True, and A or
B
is True if either A or B is True (or both are True). These can be used to
create interesting questions.
>>> varA = True
>>> varB = False
>>> varA and varB
False
>>> varA or varB
True
>>> (6 > 3) or (6 < 3)
True
>>> varA = 10
>>> varA > 0 and varA < 20
True
>>>
We can use parentheses to build up even bigger questions, but I will leave that for your experimentation!
Short circuits¶
These are usually bad, especially if you are wiring a house. In programming, they are a good thing.
What is the value of 10 / 0? Let’s see:
>>> 10 / 0
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ZeroDivisionError: division by zero
Looks like a problem. What if we want to divide by a variable, but only if the variable is not zero?
The and
and or
operators stop processing as soon as they know what the
answer will be.
For the
and
, if the first part ends upFalse
, stopFor the
or
, if the first part isTrue
, stop
This can be handy:
if x != 0 and 10/x:
# do something
In this statement, if x
has a value of zero, the first part of the logical
expression ends up False
, so we do not evaluate the second part, and we
avoid the error! .
Let’s work through an example!
The dreaded grade calculator¶
You are in school, so you just know that someone is going to assign a letter grade for your courses. Let’s see if we can build a simple program to figure out what grade to assign.
What would a student do?¶
Probably this:
# Student's view of grading
letter_grade = 'F'
grade = input('Enter the final average: ')
grade = int(grade) # need to convert the string to an integer
if grade >= 0:
letter_grade = 'A'
print('You got an ',letter_grade, 'in this course!')
Looks pretty good, I suspect you are thinking. (Actually, most of you would eliminate the test and just assign the grade. Sorry, that is not allowed!)
So, how will this work?
If you enter any grade with a numerical value of zero of higher, you get an
A
, otherwise you flunked the course. (How could that happen!)
Only allowing legal grades¶
Not very realistic, I am afraid. We need to do the job right. Let’s try to restrict legal grades to a value between 0 and 100:
# Grading program with range checking
grade = input('Enter the final average: ')
grade = int(grade)
if grade < 0 or grade > 100:
print('Average of ', grade, ' is illegal, try again!')
Notice how we checked the grade to see if it is in the range of legal values. We could do this other ways. Try these and make sure you believe they are right:
if grade >= 0 and grade <= 100:
# grade is legal
Watch for common mistakes¶
What is wrong with this test?
if grade >= 0 or grade <= 100:
# grade is legal
Think about it. The first test allows any number greater than or equal to zero
to be legal, the second on lets any number less than or equal to 100 be legal
as well. If we combine these two tests with an or
, any number ends up being
legal. That is not what we want.
Keep it simple!¶
We could try this one, which is way too complicated! It sets up a logical
variable
that is used to decide if the entered grade is legal.
is_legal = True
if grade < 0:
is_legal = False
if grade > 100
is_legal = False
if not is_legal:
print('Illegal grade')
That not
reverses the value of what comes next (a logical value).
Stopping the code if the value is illegal¶
At this point, we know the grade is legal. But how do we keep from computing the letter grade if the average is illegal?
Well, we could wrap up the calculation in an else
statement (indenting
correctly, of course. There is another way:
import sys
grade = 200
if grade < 0 or grade > 100:
print('Average of', grade, 'is illegal!')
sys.exit()
Now we need to figure out the letter grade the “real way!
if grade >= 90:
letter_grade = 'A'
This will work fine. We already know the grade is not above 100, so any grade from 90-100 will get an ‘A’. What about everything else?
Try this:
if grade >= 80:
letter_grade = 'B'
If we add this code after the first test, what will happen? You should see that this is wrong. If the grade is 95, this test is still going to be satisfied, so we will end up with a ‘B’. The solution is to only do this test if the first one fails:
if grade >= 90:
letter_grade = 'A'
else:
if grade >= 80:
letter_grade
I suspect you can see how to complete this code.
Notice anything annoying? If we keep adding tests for the other grades, the
code will be sliding off to the right, and that looks bad. Python has a
variation for this situation called the elif
clause:
if grade >= 90:
letter_grade = 'A'
elif grade >= 80:
letter_grade = 'B'
...
else:
letter_grade = 'F'
This looks much better.
Hmmm, Santa just stopped by!¶
Santa says that if you are on the “nice” list (meaning you have an attendance average above 75%, you get the next letter grade if you are within two points of the boundary. How would we add this feature into the code?
Well, we could go into our code and change all the number, but what if Santa
decides to change the number from 2 to 3 (or even 1 on a bad day)? It would be
better to use a santa_factor
to change the code. We could set this at the
top of the program, or even read it in. Here is what we might do:
if grade >= (90 - santa_factor):
letter_grade = 'A'
That looks much better.
Note
Programming pops up in interesting ways, and understanding how to form complex “questions” like this can make things easier. In my real gradebook, with is in Excel, I place a “formula” that converts your numerical average into a letter grade using exactly the kind of code you see above. Sure my gradebook could be a Python program, but Excel is the “right tool for this job”. One thing you might be surprised by, though, is that I set up that gradebook using Python. I have a program that reads a list of students from one data file, a list of assignments I will grade from another data file, and generates the spreadshet I need with all those magic “formulas” in place. All I need to do is come up with the scores for everything.