Week 3: branching, booleans
Announcements
-
Lab 2 available now.
-
Quiz 1 Study guide available on course home page.
-
Quiz 1 Friday at start of class.
-
Wednesday Ninja session good time to review.
Monday
branching
Branching allows our programs to take different paths, depending on input. For example:
$ python3 grades.py your numerical grade? 95 That's an A! $ python3 grades.py your numerical grade? 88 Good work. That's a B. $ python3 grades.py your numerical grade? 72 C
Depending on what the user enters, we get different output.
Here’s one way to write this program:
grade = float(input("your numerical grade? "))
if grade >= 90:
print("That's an A!")
elif grade >= 80:
print("Good work.")
print("That's a B.")
elif grade >= 70:
print("C")
elif grade >= 60:
print("D")
else:
print("F")
This is a 5-way branch. In general, all branches start with the if condition:
statement, but can have 0 or more elif condition:
statements, and may or may not have
and else:
statement.
After each of the above statements (if, elif, and else) comes
a code block: one or more indented lines. If the condition in any
of the if\elif
statements is True
, the corresponding code block is
executed, and no further statements are tested.
For example, in the above code, if the grade is 95, the first if
condition is tested (grade >= 90
), it evaluates to True
, so the
print("That’s an A!")
code block is run. No other conditions are
tested since the first one was True
. If there were additional code
after this 5-way branch, it would run immediately after the
print("That’s an A!")
statement.
Boolean Logic and Relational Operators
To evaluate conditions as either True or False, we need a new data type.
The Boolean or bool
type can only hold two possible values: True
or
False
. Note in Python, both of these values begin with an upper case letter
and the values do not have quotes around them. The value "True"
(with quotes)
is a string, not a Boolean.
One way to generate a Boolean value is to use one of the relational operators
listed below. For example, the operator <
compares two variables or expressions
left < right
. If the value of left
is smaller than right
, the expression left <
right
evaluates to True
, otherwise, the answer is False
.
Python’s relational operators are:
Operator | Meaning |
---|---|
< |
less than |
<= |
less than or equal to |
> |
greater than |
>= |
greater than or equal to |
== |
equal to |
!= |
not equal to |
Note that to check if two expressions are equal, you must use the ==
, e.g.,
x == 7
. Using x = 7
in Python has a different semantic meaning — it
performs a variable assignment and stores the value of 7 in the container
labeled x.
Here are some quick examples:
>>> name = "Jeff" >>> name == "jeff" False >>> x = 5 >>> x > 10 False >>> x <= 10 True >>> x != 10 True
groundhog day
Let’s write this one:
$ python3 groundhog.py did the groundhog see his shadow? yes Bleh. 6 more weeks of winter. $ python3 groundhog.py did the groundhog see his shadow? no Yay! Spring is coming early this year!
This is a simple two-way branch. One way to write this is:
sawshadow = input("did the groundhog see his shadow? ")
if sawshadow == "yes":
print("Bleh. 6 more weeks of winter.")
else:
print("Yay! Spring is coming early this year!")
This works for the above examples. What happens if the user enters "zebra"? Here’s a second, slightly better version:
sawshadow = input("did the groundhog see his shadow? ")
if sawshadow == "yes":
print("Bleh. 6 more weeks of winter.")
elif sawshadow == "no":
print("Yay! Spring is coming early this year!")
else:
print("huh?")
What happens if the user enters "Yes"?
your turn
How about this one?
$ python voting.py Enter your age on election day: 20 You are eligible to vote $ python voting.py Enter your age on election day: 18 You are eligible to vote $ python voting.py Enter your age on election day: 2 You can't vote this year You will need to wait 16 years
Wednesday
review from last time
"""
Kevin's voting example
"""
def main():
age = int(input("Age on election day? "))
if age >= 18:
print("You are eligible to vote!")
else:
print("You can't vote this year.")
howlong = 18 - age
print("You will need to wait",howlong,"years")
main()
What if I want to add an additional note if they are close to being able to vote. For example, if they are within 2 years of being able to vote, let’s print out "Soon!".
def main():
age = int(input("Age on election day? "))
if age >= 18:
print("You are eligible to vote!")
else:
print("You can't vote this year.")
howlong = 18 - age
print("You will need to wait",howlong,"years")
if howlong <= 2:
print("Soon!")
See how we can nest statements inside other code blocks?!
Also see how the last if
statement is by itself (i.e., no elif
or
else
statements with it).
Logical Operators
In many programs, it’s convenient to ask compound questions or require multiple
conditions be True
before executing some code. In these cases, we can
join two conditions together using a logical operator:
Operator | Meaning |
---|---|
and |
both boolean expressions must be true |
or |
at least one of the two boolean expressions must be true |
not |
negates the boolean value |
Below is a truth table, where x
and y
represent Boolean values or
expressions. For example, x
could be age >= 18
and y
could be status ==
"Yes"
. Each row should be read as follows: for the given Boolean values of
x
and y
, what is the result of x and y
, x or y
, and not x
:
x | y | x and y | x or y | not x |
---|---|---|---|---|
True |
True |
True |
True |
False |
True |
False |
False |
True |
False |
False |
True |
False |
True |
True |
False |
False |
False |
False |
True |
Let’s make the groundhog program a little better, and accept more than just "yes" or "no":
sawshadow = input("did the groundhog see his shadow? ")
if sawshadow == "yes" or sawshadow == "y" or sawshadow == "Yes":
print("Bleh. 6 more weeks of winter.")
elif sawshadow == "no" or sawshadow == "n" or sawshadow == "No":
print("Yay! Spring is coming early this year!")
else:
print("huh?")
Substrings, in
operator
A substring of a string is a portion of the string that appears contiguously. For example, "blue" is a substring of "blueberries". Python has some commands for accessing substrings.
The most relevant for us right now is the in
operator (not to be
confused with the in
in for i in range(..)
). The in
(membership)
operator takes a substring called the pattern and another string
commonly called the target, and returns True
if and only if the pattern
appears as a substring in the target.
Sometimes, the pattern can be a single letter, but in general, the pattern can be any string.
>>> 'a' in "apples" True >>> 'b' in "apples" False >>> "vark" in "Aardvark" True >>> "bbrries" in "blueberries" False
You can also use the in
operator on lists:
>>> names = ["jeff","lauri","kevin","andy"] >>> "JEFF" in names False >>> "jeff" in names True >>> L = [1,2,3,4,5] >>> 99 in L False >>> 3 in L True
Note that the in
operator is just checking each item in the list,
as we could do with a for
loop:
FOUND = False
for name in names:
if name=="jeff":
FOUND = True
print(FOUND)
The above uses a boolean flag variable (FOUND
), but does
exactly the same thing as "jeff" in names
.
Finally, here’s an even better version of our groundhog program, using
the in
operator:
yes = ["yes","y","Yes","YES"] # add as many as you want...
no = ["no","n","No","NO"]
sawshadow = input("did groundhog see his shadow? ")
if sawshadow in yes:
print("Bleh. 6 more weeks of winter.")
elif sawshadow in no:
print("Yay! Spring is coming early this year!")
else:
print("huh?")
your turn
Can you write this program, to count how many double letters are in a given string?
$ python3 doubles.py text: hello there is 1 double in 'hello' $ python3 doubles.py text: we love comp sci! there are 0 doubles in 'we love comp sci!' $ python3 doubles.py text: mississippi there are 3 doubles in 'mississippi'
Friday
Quiz 1!
Review of doubles.py
.
string formatting
motivation
Adding strings together and converting from ints or floats to strings
gets tiresome for anything but simple strings. We could use the comma to
print multiple items (e.g., print(x,y,z)
) but that automatically adds
a space, and doesn’t allow for precision. String formatting allows more
control over the result, by specifying both width and precision.
Assume you have three variables:
num = 6 factor = 7 answer = num*factor
Using string concatenation, the print
statement looks like this:
print(str(num) + " x " + str(factor) + " = " + str(answer))
Using string formatting, we avoid all of the str()
calls:
print("%d x %d = %d" % (num,factor,answer))
And if the width of the integers changes (double or triple-digits), we can use the width specification to make everything line up! Without specifying the width, our times table looks like this:
6 x 1 = 6 6 x 2 = 12 6 x 3 = 18 6 x 4 = 24 6 x 5 = 30 6 x 6 = 36 6 x 7 = 42 6 x 8 = 48 6 x 9 = 54 6 x 10 = 60 6 x 11 = 66 6 x 12 = 72
Notice the 54
and the 60
don’t line up, because the 9
and the 10
have
a different number of digits.
Using a width of two:
print("%2d x %2d = %2d" % (num,factor,answer))
makes the numbers in the table line up (all single-digit numbers are padded with one space):
6 x 1 = 6 6 x 2 = 12 6 x 3 = 18 6 x 4 = 24 6 x 5 = 30 6 x 6 = 36 6 x 7 = 42 6 x 8 = 48 6 x 9 = 54 6 x 10 = 60 6 x 11 = 66 6 x 12 = 72
syntax
There are three format-specifiers we normally use:
%f for floats %s for strings %d for (decimal) integers (I think %i also works for ints)
In the print statement, the format-specifiers get replaced by data, which is given at the end, like this:
print("%d x %d = %d" % (num,factor,answer))
In the above example, the first %d
is replaced by the data stored in
the variable num
, the second is replaced by factor
, and the third by
answer
.
Each format-specifier has an optional width, so, for example, you could
print out 8-character usernames with %8s
, padding the usernames that
have less than 8 characters (like jknerr1) with spaces.
Floats have an additional precision specifier, so %7.2f
means a width
of 7 (including the decimal point), with 2 digits of precision (after the decimal).
examples
Making usernames line up:
>>> unames = ["ewu1","jtaylor5","dpike1","craty1","wchou1"] >>> for name in unames: ... print("%8s" % (name)) ... ewu1 jtaylor5 dpike1 craty1 wchou1
Making numbers line up, with 2 digits of precision:
>>> nums = [3.14159, 145.7, 2, 2000, 2.1234567] >>> for num in nums: ... print("Cost of item: $%7.2f" % (num)) ... Cost of item: $ 3.14 Cost of item: $ 145.70 Cost of item: $ 2.00 Cost of item: $2000.00 Cost of item: $ 2.12
your turn!
Suppose you have parallel lists, meaning the first item in the first list corresponds to the first item in the second list, and so on. Here are parallel lists for elements and their atomic masses:
def main():
elements = ["H","He","Li","Be","B","C"]
amass = [1.0079,4.0026,6.941,9.0122,10.811,12.0107]
for i in range(len(elements)):
print(elements[i],amass[i])
main()
Currently they print out OK, but things don’t quite line up:
$ python3 periodictable.py H 1.0079 He 4.0026 Li 6.941 Be 9.0122 B 10.811 C 12.0107
If you run update21
you should get the above periodictable.py
file
in your w03-branching
directory. Add string formatting to the
print()
statement to make everything line up like this:
$ python3 periodictable.py H 1.0079 He 4.0026 Li 6.9410 Be 9.0122 B 10.8110 C 12.0107
Comparing Strings
We can compare string values just as we can compare ints. That is, we can use any relational operator on a pair of a strings.
"Aardvark" < "Baboon"
Strings are compared lexicographically (i.e., based on their sorted
dictionary order). So, the above expression is True
because Aardvark
appears earlier in the dictionary.
Drilling down, Python actually compares the two strings character-by-character until it finds a difference. So, it will first compare A to B. It finds that they are different, and so it returns True. If the expression is:
"Apple" < "Applied"
Python first compares the As, then each p, then the ls, and finally
stops at the next position since e and i are different. Since e comes
first in the alphabet, it returns True
.
What if we had:
"apple" < "APPLE"
What does Python do? To drill down even further, everything in the
computer is represented numerically in binary (0s and 1s). So, even text
is really represented as a series of numbers (positive integers,
specifically). The encoding, or conversion, is known as Unicode. We can
find the conversion using the ord()
function:
$ python3 Python 3.5.2 (default, Nov 23 2017, 16:37:01) [GCC 5.4.0 20160609] on linux Type "help", "copyright", "credits" or "license" for more information. >>> ord('A') 65 >>> ord('B') 66 >>> ord('Z') 90 >>> ord('!') 33
So to answer our question above, we need to compare the Unicode value of
'a' to 'A'. 'A' is a small Unicode value, so the expression is False
.
We can also convert in the other direction - from a number to a character using the chr() function:
>>> chr(58) ':' >>> chr(100) 'd' >>> chr(75) 'K'