8. If/Else
Activity
Using only what we have learned so far, write a function smush(a_number)
that checks if the number is
positive or negative. If a_number
is positive, the function will return half the value of a_number
. If
the number is not positive, simply return a_number
.
8.1. Conditional Expressions
Booleans were discussed in the previous topic
We discussed them as a type
How to use Boolean operators
How to use comparison operators
But Booleans allow us to do a lot more than just evaluate an expression to
True
/False
Consider
smush
from the above activityWe know how to ask if
a_number
is positivea_number > 0
The key now is to tell Python that,
if
some condition isTrue
, do something
1def smush(a_number: float) -> float:
2 """
3 Returns half the value of the parameter a_number if the value is positive,
4 otherwise, return the value of a_number.
5
6 :rtype: float
7 :param a_number: Some arbitrary number.
8 :return: Half of a_number when it is positive, a_number when not positive.
9 """
10 return_value = a_number
11 if a_number > 0:
12 return_value = a_number / 2
13 return return_value
14
15
16# Tests for smush
17assert 5 == smush(10)
18assert -10 == smush(-10)
19assert 0 == smush(0)
In the above example we made use of an
if
statementWhen the evaluated Boolean value after the
if
isTrue
, the indented code is executed, otherwise the indented code block is ignoredIf we follow the code within the function when
a_number
is10
, the execution is as followsAssign the value of
a_number
(10
) toreturn_value
Evaluate
a_number > 0
10 > 0
True
Since the expression evaluated to
True
, the indented code is runDivide
return_value
by2
and assign it back intoreturn_value
Return the value stored in
return_value
(5
)
Similarly, if we follow the code when
a_number
is-10
, the execution is as followsAssign the value of
a_number
(-10
) toreturn_value
Evaluate
a_number > 0
-10 > 0
False
Since the expression evaluated to
False
, the indented code is skippedReturn the value stored in
return_value
(-10
)
Activity
Write a function is_negative(a_number)
that returns True
if a_number
is negative and False
otherwise.
8.2. Compound Conditions
When we make use of an
if
statement, the value being checked needs to ultimately be evaluated to a BooleanThis means that we can make use of more complex compound Boolean expressions
Comparison operators
Arithmatic operators
Boolean operators
1def three_five_divisible(a_number: float) -> str:
2 """
3 Checks if a number is divisible by both three and five. If it is, return
4 a string "It is!", otherwise "Nope".
5
6 :rtype: str
7 :param a_number: Some arbitrary number.
8 :return: String indicating if the number is divisible by three and five
9 """
10 if a_number % 3 == 0 and a_number % 5 == 0:
11 return "It is!"
12 return "Nope"
13
14
15# Tests for three_five_divisible
16assert "It is!" == three_five_divisible(0)
17assert "It is!" == three_five_divisible(15)
18assert "It is!" == three_five_divisible(-30)
19assert "Nope" == three_five_divisible(3) # Divisible by 3 but not 5
20assert "Nope" == three_five_divisible(-50) # Divisible by 5 but not 3
21assert "Nope" == three_five_divisible(1) # Divisible by neither
Note
The modulo operator %
(often called just “mod”) effectively does division and returns the remainder. For
example, 10 % 3
is 1
since 10/3
is 3
remainder 1
.
In the three_five_divisible
example, we are checking if the remainder of the division is 0
, which would mean
that the value can be evenly divided.
Another common use of %
is checking if a value is even or not — x % 2
is 0
when x
is even since
it would mean that x
can be evenly divided by 2
.
The above function
three_five_divisible
needs to check if a number is divisible by 3and
5This means that there are two conditions we need to check for being
True
If we follow the code within the function when
a_number
is15
, the execution is as followsEvaluate
a_number % 3 == 0 and a_number % 5 == 0
15 % 3 == 0 and 15 % 5 == 0
0 == 0 and 0 == 0
True and True
True
Since the expression evaluated to
True
, the indented code is runReturn
"It is!"
, function ends
If we follow the code within the function when
a_number
is9
, the execution is as followsEvaluate
a_number % 3 == 0 and a_number % 5 == 0
9 % 3 == 0 and 9 % 5 == 0
0 == 0 and 4 == 0
True and False
False
Since the expression evaluated to
False
, the indented code is skippedReturn
"Nope"
8.3. Alternative Execution
This pattern is very common
1if x > 10:
2 do_something()
3if not(x > 10):
4 do_something_else()
When we have an either/or situation we make use of
else
1if x > 10:
2 do_something()
3else:
4 do_something_else()
The two examples above will effectively do the same thing, but the 2nd is nicer
Write less
Intuitive and easy to read/understand
Eliminate potential bugs
Activity
Write a function called hail
that takes an integer as an argument. If the integer is even, return the value of
that integer divided by 2. If it is odd, return the value multiplied by 3 and with one added. In other words,
given a number \(n\), return \(n/2\) when it is even and \(3n + 1\) when it is odd. Hint: Don’t
forget about %
.
This is actually some neat math stuff. Isn’t it cool that we’re writing a Python function that’s doing exactly what the math is saying?
Note
If we revisit smush
, we can rewrite the function in a few different ways that are all correct.
1def smush_version_2(a_number: float) -> float:
2 """
3 Returns half the value of the parameter a_number if the value is positive,
4 otherwise, return the value of a_number.
5
6 :rtype: float
7 :param a_number: Some arbitrary number.
8 :return: Half of a_number when it is positive, a_number when not positive.
9 """
10 if a_number > 0:
11 return_value = a_number / 2
12 else:
13 return_value = a_number
14 return return_value
In smush_version_2
, an else
is used and the function has only one return
. The use of the else
here
is not required (as seen in the original smush
), but the use of else
in this situation may make the function
a little clearer. Additionally, some programmers prefer having their functions have only one return
, but this is
by no means more correct.
1def smush_version_3(a_number: float) -> float:
2 """
3 Returns half the value of the parameter a_number if the value is positive,
4 otherwise, return the value of a_number.
5
6 :rtype: float
7 :param a_number: Some arbitrary number.
8 :return: Half of a_number when it is positive, a_number when not positive.
9 """
10 if a_number > 0:
11 return a_number / 2
12 else:
13 return a_number
Another possibility is smush_version_3
. You will notice how similar it is to version 2, but here we use two
return
s in the if
and else
blocks. Again, this is not more correct and it is only shown here to
demonstrate how the same functionality can be implemented differently.
8.4. Exclusive Alternatives
Sometimes we need to check various conditions and
if
/else
isn’t good enoughFor example, what if I want a function to take a percentage grade and return a letter grade
1def letter_grade_broken(percent_grade: float) -> str:
2 """
3 Calculate the letter grade associated with the provided percent grade.
4
5 :rtype: str
6 :param percent_grade: A grade as a percent
7 :return: Letter grade for the provided percentage
8 """
9 letter_grade = ""
10 if percent_grade >= 90:
11 letter_grade = "A+"
12 if percent_grade >= 80:
13 letter_grade = "A"
14 if percent_grade >= 70:
15 letter_grade = "B"
16 if percent_grade >= 60:
17 letter_grade = "C"
18 if percent_grade >= 50:
19 letter_grade = "D"
20 else:
21 letter_grade = "F"
22 return letter_grade
The above example
letter_grade_broken
may be one of the first ideas you come up with, but unfortunately it has a problemIf we run
assert "A+" == letter_grade_broken(99)
letter_grade_broken(99)
would actually return"D"
The trick to understanding the problem is to take our time and look at the code
Call
letter_grade_broken(99)
percent_grade
is assigned the value99
Check if
percent_grade >= 90
percent_grade >= 90
99 >= 90
True
Since the expression is evaluated to
True
, the indented code is runAssign
letter_grade
the value"A+"
The execution continues
Check if
percent_grade >= 80
percent_grade >= 80
99 >= 80
True
Since the expression is evaluated to
True
, the indented code is runAssign
letter_grade
the value"A"
…
The trouble here is that we really only want one of these
if
code blocks to runWe want them to be mutually exclusive alternatives
There are a few ways one could fix this
Have a
return
in each indented block since that would stop execution of the function once areturn
is reachedReverse the order of the
if
sCheck upper and lower bounds (e.g.
percent_grade >= 80 and percent_grade < 90
)
But arguably the better way to address this is with
elif
sCan be read as else, if…
These allow us to have at most one of the code blocks in the chain of conditions to run
In other words, as soon as one of the
if
s is true, all otherif
s are skipped and the program continues running after theelse
When using
elif
s, always end with a finalelse
1def letter_grade(percent_grade: float) -> str:
2 """
3 Calculate the letter grade associated with the provided percent grade.
4
5 :rtype: str
6 :param percent_grade: A grade as a percent
7 :return: Letter grade for the provided percentage
8 """
9 letter_grade = ""
10 if percent_grade >= 90:
11 letter_grade = "A+"
12 elif percent_grade >= 80:
13 letter_grade = "A"
14 elif percent_grade >= 70:
15 letter_grade = "B"
16 elif percent_grade >= 60:
17 letter_grade = "C"
18 elif percent_grade >= 50:
19 letter_grade = "D"
20 else:
21 letter_grade = "F"
22 return letter_grade
8.5. Nesting Conditionals
You can “nest” conditionals inside other conditionals
1# Find quadrant with 'nested If's
2if x > 0:
3 if y > 0:
4 print("First Quadrant")
5 else:
6 print("Fourth Quadrant")
7else:
8 if y > 0:
9 print("Second Quadrant")
10 else:
11 print("Third Quadrant")
For simplicity, ignore point \((0,0)\) being in the third quadrant
In the above example, we could have done it without nesting by using
and
sBut some may find the nested version of the code more intuitive and readable
1# Find quadrant with 'and's
2if x > 0 and y > 0:
3 print("First Quadrant")
4elif x > 0 and y < 0
5 print("Fourth Quadrant")
6elif x < 0 and y > 0:
7 print("Second Quadrant")
8else:
9 print("Third Quadrant")