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/FalseConsider
smushfrom the above activityWe know how to ask if
a_numberis positivea_number > 0
The key now is to tell Python that,
ifsome 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
ifstatementWhen the evaluated Boolean value after the
ifisTrue, the indented code is executed, otherwise the indented code block is ignoredIf we follow the code within the function when
a_numberis10, the execution is as followsAssign the value of
a_number(10) toreturn_valueEvaluate
a_number > 010 > 0True
Since the expression evaluated to
True, the indented code is runDivide
return_valueby2and assign it back intoreturn_valueReturn the value stored in
return_value(5)
Similarly, if we follow the code when
a_numberis-10, the execution is as followsAssign the value of
a_number(-10) toreturn_valueEvaluate
a_number > 0-10 > 0False
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
ifstatement, 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_divisibleneeds to check if a number is divisible by 3and5This means that there are two conditions we need to check for being
TrueIf we follow the code within the function when
a_numberis15, the execution is as followsEvaluate
a_number % 3 == 0 and a_number % 5 == 015 % 3 == 0 and 15 % 5 == 00 == 0 and 0 == 0True and TrueTrue
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_numberis9, the execution is as followsEvaluate
a_number % 3 == 0 and a_number % 5 == 09 % 3 == 0 and 9 % 5 == 00 == 0 and 4 == 0True and FalseFalse
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
returns 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/elseisn’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_brokenmay 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_gradeis assigned the value99Check if
percent_grade >= 90percent_grade >= 9099 >= 90True
Since the expression is evaluated to
True, the indented code is runAssign
letter_gradethe value"A+"The execution continues
Check if
percent_grade >= 80percent_grade >= 8099 >= 80True
Since the expression is evaluated to
True, the indented code is runAssign
letter_gradethe value"A"…
The trouble here is that we really only want one of these
ifcode blocks to runWe want them to be mutually exclusive alternatives
There are a few ways one could fix this
Have a
returnin each indented block since that would stop execution of the function once areturnis reachedReverse the order of the
ifsCheck upper and lower bounds (e.g.
percent_grade >= 80 and percent_grade < 90)
But arguably the better way to address this is with
elifsCan 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
ifs is true, all otherifs are skipped and the program continues running after theelseWhen using
elifs, 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
andsBut 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")