Table content of this notebook.
pass
statement and dropping else
daysInMonth
and
and or
In the previous lecture, we discussed relational operators. They are binary operators (take two operands, one on each side) and evaluate to a boolean value of True
or False
. We discussed six operators: >
, >=
, <
, <=
, ==
, !=
.
Predict the values of the following expressions:
7 > 9
8 != 10
'bunny' < 'cat'
'do' >= 'dog'
'A' == 'a'
In the previous lecture, we also introduced three logical operators: and
, or
, and not
. The first two are binary operators (they take two operands) while not
is a unary operator (it takes only one operand). Usually, these operators take as their operands boolean values that are the output of relational expressions and predicates. For example:
10 > 5 and 10 < 20
'a' > 'b' or 'b' < 'c'
not 10 > 100 # Intepreted as not (10 > 100)
Finally, in lecture 6 we also saw that a predicate is simply a function that returns a boolean value. Here are some sample predicates:
def isFreezing(temp):
return temp <= 32
def isBoiling(deg):
return deg >= 212
def isWaterLiquidAtTemp(t):
return (not isFreezing(t)) and (not isBoiling(t))
Before running the examples below, try to first guess their output.
isFreezing(20)
isBoiling(100)
isWaterLiquidAtTemp(72)
In programs, control is where the computer is currently executing code. Think of it as a "you are here" arrow in the code, much like a "sing this word" indicator in a karaoke song.
We imagine that control flows through a program, executing statements one-by-one along the way. This is visualized by control flow diagrams with arrows showing how control flows from one statement to another. For example, for the sequence of the three Python statements
ageString = input('Enter your age> ') # statement1
ageInt = int(ageString) # statement2
print('In ten years, you will be ' + str(ageInt + 10) + ' years old') #statement3
the control flow looks like this:
An if
statement (also called a conditional statement) chooses between two branches based on a test value.
Conditional statements can be understood as branching flow in control flow diagrams. The meaning of the if
statement
if bool_expression:
statement1
statement2
statement3
else:
statement4
statement5
can be understood by the following control flow diagram:
Note that (1) only one of the two branches can be taken and (2) each branch can have multiple statements
def branchingFlow(boolVal):
if boolVal:
print("statement1")
print("statement2")
print("statement3")
else:
print("statement4")
print("statement5")
branchingFlow(True)
branchingFlow(False)
Either or both branches in a conditional statement may have return
statements that cause a return from an enclosing function:
def branchingFlowReturn(boolVal):
if boolVal:
print("statement1")
print("statement2")
print("statement3")
return('trueBranch')
else:
print("statement4")
print("statement5")
return('falseBranch')
branchingFlowReturn(True)
branchingFlowReturn(False)
def absolute(n):
'''Return the absolute value of the number n'''
if n >= 0:
return n
else:
return -n
def classify(num):
'''Return a string indicating whether num is negative or not.'''
if num < 0:
return 'negative'
else:
return 'nonnegative'
absolute(-17)
absolute(111)
classify(-17)
classify(111)
A function with a conditional might print something.
def doWhenTemperature(temp):
if temp <= 65:
print("Put on a sweater or coat.")
else:
print("You can wear short sleeves today.")
doWhenTemperature(72)
doWhenTemperature(50)
Does doWhenTemperature
return anything?
print(doWhenTemperature(50))
def doILikeMissyElliott(rap, hiphop, pop):
"""A predicate to determine whether you like Missy Elliott"""
return rap or (hiphop and pop) # parentheses are unnecessary but used for clarity
def musicRecommender(rap, hiphop, pop):
"""Simple function that prints """
if doILikeMissyElliott(rap, hiphop, pop):
print("You should listen to Missy Elliott.")
else:
print("I would avoid Missy Elliott.")
musicRecommender(True, False, False)
musicRecommender(False, False, True)
musicRecommender(False, True, False)
Both function bodies and conditional branches can contain multiple statements. Because condtional statements are just one kind of statements, a sequence of statements can contain multiple conditional statements.
def categorize(num):
'''This function has 3 statements in its body.
They are executed from top to bottom, one after the other.
'''
print('Categorizing', num)
if num % 2 == 0:
print("It's even")
else:
print("It's odd")
if num < 0:
'''This branch has 2 statements.'''
print("It's negative")
print("(That means it's less than zero)")
else:
print("It's nonnegative")
categorize(111)
categorize(-20)
pass
statement and dropping else
¶When we don't want to do anything in a conditional branch, we use the special pass
statement, which means "do nothing". (It's a syntax error to leave a branch blank.)
def warnWhenTooFast(speed):
if speed > 55:
print("Slow down! You're going too fast")
else:
pass # do nothing
warnWhenTooFast(75)
warnWhenTooFast(40)
It's OK to have an if
statement without an else
clause. In this case, the missing else
clause is treated as if it were a pass
statement.
def warnWhenTooFast2(speed):
if speed > 55:
print("Slow down! You're going too fast")
warnWhenTooFast2(75)
warnWhenTooFast2(40)
Below are two correct variants of the absolute
function defined above. Explain why they work.
def abs2(n):
'''returns the absolute value of n'''
result = n
if n < 0:
result = -n
return result
print(abs2(-17), abs2(42))
def abs3(n):
'''returns the absolute value of n'''
if n < 0:
return -n
return n
print(abs3(-17), abs3(42))
It often make sense to have a conditional statement nested inside the branch of another conditional.
Below we show variants of a function that returns the movie rating appropriate for a given age of movier goer. (If you want to learn more about film ratings, read this Wikipedia article.)
def movieAge1(age):
"""Returns the movie rating for the given age."""
if age < 8:
return 'G'
else:
if age < 13:
return 'PG'
else:
if age < 18:
return 'PG-13'
else:
return 'R'
def test_movieAge1(age):
print("age =", age, "; rating =", movieAge1(age))
test_movieAge1(5)
test_movieAge1(10)
test_movieAge1(15)
test_movieAge1(20)
It's possible to expression the same conditional logic with different nested conditionals:
def movieAge2(age):
"""Returns the movie rating for the given age."""
if age < 13:
if age >= 8:
return 'PG'
else:
return 'G'
else:
if age >= 18:
return 'R'
else:
return 'PG-13'
def test_movieAge2(age):
print("age =", age, "; rating =", movieAge2(age))
test_movieAge2(5)
test_movieAge2(10)
test_movieAge2(15)
test_movieAge2(20)
Python uses chained (multibranch) conditionals with if
, elif
s, and else
to execute exactly one of several branches.
def movieAge3(age):
"""Returns the movie rating for the given age."""
if age < 8:
return 'G'
elif age < 13:
return 'PG'
elif age < 18:
return 'PG-13'
else:
return 'R'
def test_movieAge3(age):
print("age =", age, "; rating =", movieAge3(age))
test_movieAge3(5)
test_movieAge3(10)
test_movieAge3(15)
test_movieAge3(20)
Chained conditionals can be understood by control flow diagrams:
Important: As seen in the diagram. only the first branch that evaluates to True
will be executed.
Important: As shown in the following example, the order of chaining conditionals matters!
def movieAgeWrong(age):
if age < 18:
return 'PG-13'
elif age < 13:
return 'PG'
elif age < 8:
return 'G'
else:
return 'R'
def test_movieAgeWrong(age):
print("age =", age, "; rating =", movieAgeWrong(age))
test_movieAgeWrong(5)
test_movieAgeWrong(10)
test_movieAgeWrong(15)
test_movieAgeWrong(20)
daysInMonth
¶Define a function named daysInMonth
that takes a month (as an integer) as the argument, and returns
the number of days in it, assuming the year is not a leap year.
Assume 1 is January, 2 is February, ..., 12 is December. If the month does not fall
between 1 and 12, return an error message as a string.
Make the function as concise as possible (group months by days, don't write 12 separate if-else clauses).
# Define your daysInMonth function below
# Your code here
def daysInMonth(month):
"""Returns number of days in a month or a message of error."""
if month == 2:
return 28
elif month < 1 or month > 12:
return "This is not a valid month."
elif month == 4 or month == 6 or month == 9 or month == 11:
return 30
else:
return 31
daysInMonth(4) # April
daysInMonth(8) # August
daysInMonth(2) # February
daysInMonth(13) # Error message
Having seen conditional statements, you may be tempted to use them in predicates. But most predicates can be defined without conditionals by using combinations of relational and logical operators. For example, compare the complicated and simplifed functions below:
def isFreezingComplicated(temp):
if temp <= 32:
return True
else:
return False
def isFreezingSimplified(temp):
return temp <= 32
print(isFreezingComplicated(20), isFreezingComplicated(72))
print(isFreezingSimplified(20), isFreezingSimplified(72))
def isPositiveEvenComplicated(num):
if num > 0:
if num % 2 == 0:
return True
return False
return False
def isPositiveEvenSimplified(num):
return num > 0 and num % 2 == 0
print(isPositiveEvenComplicated(42), isPositiveEvenComplicated(17), isPositiveEvenComplicated(-36))
print(isPositiveEvenSimplified(42), isPositiveEvenSimplified(17), isPositiveEvenSimplified(-36))
What happens if we compare numerical to string values?
In Python 3, numerical and string values are never equal. Furthermore, attempting to use >, >=, <=, or < will result in a TypeError.
Try to guess the outputs of these relational expressions before running the cells.
7 == '7'
10 < '10'
10000000 >= '0'
What happens if we compare numerical to boolean values?
In Python 3, in comparisons involving numbers and booleans, False is considered a synonym for 0 and True is considered a synonym for 1.
0 == False
1 == True
2 > True
1 > True
1 > False
0 > False
What to take away from the above examples: Python 3 allows the comparison of values of different types in some contexts. In general, it's better to stick with comparing values of the same type.
In Python, not
has surprising behavior when given a non-boolean operand:
not 111
not 0
not 'ab'
not ''
Truthy vs. Falsey Values:
What's going on in the above examples?
In Python, it turns out that in many contexts where a boolean is normally expected, 0 and 0.0 are treated like False
and all other numbers are treated like True
. So not 0
evaluates to True
and not 111
evaluates to False
.
Similarly, the empty string is treated like False
and nonempty strings are treated like True
. So not ''
evaluates to True
and not 'ab'
evaluates to False
.
In contexts where a boolean is normally expected, values that act like True
are called Truthy and values that act like False
are called Falsey. So 0, 0.0, ''
and False
are Falsey values, while all other numbers, strings, and booleans are Truthy.
Sadly, things are more complicated when it comes to testing equality:
0 == False
'' == False # Only 0 and 0.0 are considered equal to False
1 == True
17 == True # Only 1 and 1.0 are considered equal to True
'abc' == True
and
and or
also behave in surprising ways when their operands are not booleans:
111 or 230 # If first value is Truthy, return it; otherwise return second
0 or 230
0 or 0.0
'cat' or 'dog'
'' or 'dog'
0 or ''
111 and 230 # If first value is Falsey, return it; otherwise return second
0 and 230
0 and 0.0
'cat' and 'dog'
'' and 'dog'
0 and ''
The following definition of isVowel
doesn't work. Explain why!
def isVowelWrong(s):
low = s.lower()
return low == ('a' or 'e' or 'i' or 'o' or 'u')
isVowelWrong('a') # This works
isVowelWrong('b') # This works
isVowelWrong('e') # This doensn't work. Why?
Solution notes go here:
Because 'a'
is Truthy, 'a' or 'e'
evaluates to 'a'
. Similarly, ('a' or 'e' or 'i' or 'o' or 'u')
is equivalent to 'a'
, and return low == ('a' or 'e' or 'i' or 'o' or 'u')
is equivalent to return low == 'a'
!
and
and or
¶Not only does or
return the value of its left operand if it is Truthy; in this case the right operand is not even evaluated! This is called short-circuit evaluation of or
:
(2 < 3) or ((1/0) > 0) # No ZeroDivisionError occurs because left operand is Truthy and right operad is never evaluated
42 or ((1/0) > 0)
(2 > 3) or ((1/0) > 0) # ZeroDivisionError occurs because left operand is False and (1/0) is evaluated in right operand
Similarly, if the left operand of and
is Falsey, it is returned immediately without even evaluating the right operand.
(2 > 3) and ((1/0) > 0)
0 and ((1/0) > 0)
'' and ((1/0) > 0)
(2 < 3) and ((1/0) > 0)