Conditionals¶
Movie scripts vs. Choose Your Own Adventure¶
So far, we've been writing programs that resemble scripts for movies: we define a set of steps to take, and then we walk through them one by one, with the same path and outcome each time.
Today, we'll look at conditional execution, which will let us write programs that don't always take the same path - kind of like a Choose Your Own Adventure book.
if
statements¶
An if
statement is the most basic conditional statement in Python. It looks like this:
if <test>:
statement(s)
x = 4
if x > 5:
print("This print executes when x is greater than 5")
x = 10
if x > 5:
print("This print executes when x is greater than 5")
This print executes when x is greater than 5
x > 5
is the test. It must be a boolean expression.- A boolean expression is any expression that evaluates to
True
orFalse
- A boolean expression is any expression that evaluates to
print("This print executes when x is greater than 5")
is a statement "inside" the if.- It is conditionally executed.
- i.e. It is executed if the test
x > 5
evaluates toTrue
.
Indentation matters¶
The lines that are indented after the if <test>:
line are the lines that are conditionally executed.
The conditional execution stops when the indentation stops.
x = 6
y = 7
if x > 5 and y < 4:
print("This prints if x is greater than 5")
print("This also prints when x is greater than 5")
print("This always prints")
This always prints
print("This always prints")
will always execute, even if x is not greater than 5.
Writing tests¶
There are a number of comparison (relational) and logical (and/or/not) operators that can be used in conditionals.
(They can be used anywhere, but show up most commonly in conditionals)
Relational operators¶
Relational operators are commonly used in if
tests.
Operator | Meaning | Example | Result |
---|---|---|---|
== | equal | 1 + 1 == 2 | True |
!= | not equal | 2 + 3 != 4 | True |
< | less than | 10 < 5 | False |
> | greater than | 10 > 5 | True |
<= | less than or equal to | 1 + 1 <= 2 | True |
>= | greater than or equal to | 3 + 4 >= 6 | True |
Relational expressions¶
Evaluating an expression with a relational operator results in a boolean (True or False) value.
Some examples:
5 > 10
False
5 != 10
True
(5 + 1) == 6
True
(7 * 2) != 14
False
(6 ** 2) <= 40
True
Logical operators¶
Logical operators are also commonly used in if
tests. From highest to lowest precedence:
Operation | Meaning |
---|---|
x or y |
True if x is True OR y is True |
x and y |
True if x is True AND y is True |
not x |
True if x is False , False if x is True |
(1 > 2) or (5 > 4)
True
(1 > 2) and (5 > 4)
False
not (1 > 2)
True
if/else
statements¶
You can add an else
clause to an if
statement. The else
clause will execute if the test evaluates to False
if <test>:
statement(s)
else:
statement(s)
x = 4
if x > 5:
print("x is big!")
else:
print("x is small!")
x is small!
if/elif/else
statements¶
The last variation is "if/else-if/else".
if <test1>:
statement(s)
elif <test2>:
statement(s)
else:
statement(s)
You can have as many elif
clauses as you want. You can also omit the else
clause.
Each elif
test is only performed if all the tests before it evaluate to False
.
x = 1
if x > 100:
print("x is really big!")
elif x > 10:
print("x is kinda big")
else:
print("x is small!")
x is small!
Logic¶
There are many different ways to structure conditional execution logic. Learning to choose the best one takes practice. Let's look at a few examples.
Example 0: How big is x?¶
What if we re-ordered the if/elif clauses from previous example?
Is there any value we can set x
to that would make this code print "x is really big!"?
x = 1
if x > 10:
print("x is kinda big")
elif x > 100:
print("x is really big!")
else:
print("x is small!")
x is small!
No! If x > 100
, then x > 10
is also always True. The second branch of the if/else will never be taken.
This is the correct way to order the clauses for this example:
x = 4
if x > 100:
print("x is really big!")
elif x > 10:
print("x is kinda big")
else:
print("x is small!")
x is small!
Example 1: Grade buckets¶
Write code to convert a number grade into a letter grade.
# Attempt 1
grade = 81
if grade > 65:
print("D")
if grade > 72:
print("C")
if grade > 79:
print("B")
if grade > 89:
print("A")
else:
print("F")
D C B F
This logic is incorrect - each if
is evaluated independently. This code allows a number grade to be categorized under multiple letter grades.
# Attempt 2
grade = 81
if grade > 65:
print("D")
elif grade > 72:
print("C")
elif grade > 79:
print("B")
elif grade > 89:
print("A")
else:
print("F")
D
Still incorrect - by using if/elif/else
we now get a single letter grade, but the order of our tests leads to an incorrect answer.
# Attempt 3
grade = 81
if grade >= 90:
print("A")
elif grade >= 80:
print("B")
elif grade >= 73:
print("C")
elif grade >= 66:
print("D")
else:
print("F")
B
Now we get a correct answer!
More than one way to skin a cat¶
Here's a different (but still correct) solution:
# Attempt 4
grade = 81
if grade <= 65:
print("F")
if grade >= 66 and grade <= 72:
print("D")
if grade >= 73 and grade <= 79:
print("C")
if grade >= 80 and grade <= 89:
print("B")
if grade >= 90:
print("A")
B
More than one way to skin a cat¶
Is one better than the other?
Both are correct for the purposes of this exercise.
The if/elif/else version is slightly better style.
- An if/elif/else is guaranteed to execute exactly one case, which makes sense for bucketing a grade.
- The solution using only
if
s also has some logic duplicated - the upper and lower bounds are specified for each grade band. If the bands change, the code needs to be updated in 2 places.
Example 2: Allergies¶
# Attempt 1
nuts = "n"
shellfish = "y"
gluten = "y"
pollen = "n"
print("Don't feed me the following types of food: ")
if nuts == "y":
print("nuts")
elif shellfish == "y":
print("shellfish")
elif gluten == "y":
print("gluten")
else:
print("pollen")
Don't feed me the following types of food: shellfish
Looks ok... but wait... Can you spot the bug?
This code is not correct with more than one allergy.
# Attempt 1
nuts = "y"
shellfish = "y"
gluten = "n"
pollen = "n"
print("Don't feed me the following types of food: ")
if nuts == "y":
print("nuts")
elif shellfish == "y":
print("shellfish")
elif gluten == "y":
print("gluten")
else:
print("pollen")
Don't feed me the following types of food: nuts
The if/elif/else structure here doesn't make sense. If more than one allergy is present, the code will need to execute more than one of the cases.
# Attempt 2
nuts = "y"
shellfish = "y"
gluten = "y"
pollen = "n"
print("Don't feed me the following types of food: ")
if nuts == "y":
print("nuts")
if shellfish == "y":
print("shellfish")
if gluten == "y":
print("gluten")
if pollen == "y":
print("pollen")
Don't feed me the following types of food: nuts shellfish gluten
In this case, each test is independent. if/if/if makes sense.
Example 3: Long jump¶
# Attempt 1
world_record = 8.95
jump_distance = 9
officially_verified = "n"
if jump_distance > world_record and officially_verified == "y":
print("Congratulations, you just set a new world long jump record!")
elif jump_distance > world_record:
print("Great jump, but it's not a record without verification.")
else:
print("Good try, but not a record")
Great jump, but it's not a record without verification.
This code is correct. However, there is a more readable way to structure it.
Yet another way to skin a cat¶
world_record = 8.95
jump_distance = 9.01
officially_verified = "y"
if jump_distance > world_record:
if officially_verified == "y":
print("Congratulations, you just set a new world long jump record!")
else:
print("Great jump, but it's not a record without verification.")
else:
print("Good try, but not a record")
Congratulations, you just set a new world long jump record!
Conditional statements can be "nested" inside one another. For each level of nesting, the indentation level increases.
Code style¶
With complex conditionals, there are often many different ways to represent the same logic. Try to pick a structure that maps correctly to the logic you need, but is also easy (for you and someone else) to understand. The shortest code is not always the best.
As you gain more coding experience, your ability to balance conciseness, readability, and simplicity will improve.
Exercise: FIZZBUZZ¶
Open the class exercises Codespace
Open the file: exercises/03/fizzbuzz/README.md
, follow the instructions.
- If you don't see this folder: Open the file:
get_exercises.sh
, click the "Run" button at the top right of the editor.
Announcements¶
- PS1 due next Wednesday (1/29, 11:59pm)
- Start early so you have time to ask questions if you get stuck!
- Don't use outside sources, stick to the content from Unit 2 (Variables, Datatypes, Expressions)
- Conditionals are not needed for PS1
Exercise: Personality Quiz¶
Open the class exercises Codespace
Open the file: exercises/03/personality_quiz/README.md
, follow the instructions.
- If you don't see this folder: Open the file:
get_exercises.sh
, click the "Run" button at the top right of the editor.
Example Problem - Progressive income tax calculator¶
In a progressive tax, different portions of income are taxed at different rates:
Tax rate | Income amounts |
---|---|
10% | \$0 to \$10,275 |
12% | \$10,276 to \$41,775 |
22% | \$41,776 to \$89,075 |
24% | \$89,076 to \$170,050 |
32% | \$170,051 to \$215,950 |
35% | \$215,951 to \$539,900 |
37% | \$539,901 or more |
Example Problem - Progressive income tax calculator¶
e.g. an income of \$90,000 would be taxed:
Bracket | Equation | Tax amount |
---|---|---|
10% for the first \$10,275 | 10275 * 0.1 |
\$1027.5 |
12% of \$10,276 to \$41,775 | (41775 - 10276) * 0.12 |
\$3779.88 |
22% of \$41,776 to \$89,075 | (89075 - 41776) * 0.22 |
\$10405.78 |
24% of \$89,076 to \$90,000 | (90000 - 89076) * 0.24 |
\$221.76 |
Total | 1027.5 + 3779.88 + 10405.78 + 221.76 |
\$15434.92 |
Approach¶
Take this one step at a time.
First, the simplest case: calculate tax if income is less than $10,275
income = 8510
if income < 10275:
tax = income * 0.1
print("You owe $" + str(tax) + " on income of $" + str(income) + ". Your tax rate is " + str(tax / income * 100) + "%.")
else:
print("Your income is too high. Please see a certified tax professional.")
You owe $851.0 on income of $8510. Your tax rate is 10.0%.
Step 2¶
Add another step: handle income between \$10275 and \$41775
income = 5000
if income < 10275:
tax = income * 0.1
print("You owe $" + str(tax) + " on income of $" + str(income) + ". Your tax rate is " + str(tax / income * 100) + "%.")
elif income < 41775:
tax = (10275 * 0.1) + ((income - 10275) * .12)
print("You owe $" + str(tax) + " on income of $" + str(income) + ". Your tax rate is " + str(tax / income * 100) + "%.")
else:
print("Your income is too high. Please see a certified tax professional.")
You owe $500.0 on income of $5000. Your tax rate is 10.0%.
Readability¶
Let's improve the readability of that calculation, and also remove the duplicated print.
income = 32791
if income < 10275:
tax = income * 0.1
elif income < 41775:
bracket1 = (10275 * .1)
bracket2 = (income - 10275) * .12
tax = bracket1 + bracket2
if income < 41775:
print("You owe $" + str(tax) + " on income of $" + str(income) + ". Your tax rate is " + str(tax / income * 100) + "%.")
else:
print("Your income is too high. Please see a certified tax professional.")
You owe $3729.42 on income of $32791. Your tax rate is 11.373303650391877%.
Next case...¶
income = 75013
if income < 10275:
tax = income * 0.1
elif income < 41775:
bracket1 = (10275 * .1)
bracket2 = (income - 10275) * .12
tax = bracket1 + bracket2
elif income < 89075:
bracket1 = (10275 * .1)
bracket2 = (41775 - 10275) * .12
bracket3 = (income - 41775) * .22
tax = bracket1 + bracket2 + bracket3
if income < 89075:
print("You owe $" + str(tax) + " on income of $" + str(income) + ". Your tax rate is " + str(tax / income * 100) + "%.")
else:
print("Your income is too high. Please see a certified tax professional.")
You owe $12119.86 on income of $75013. Your tax rate is 16.157012784450696%.
Do you see a pattern?¶
Let's fill in the rest...
income = 390101
tax = 0
if income <= 10275:
tax = income * .1
elif income <= 41775:
bracket1 = (10275 * .1)
bracket2 = (income - 10275) * .12
tax = bracket1 + bracket2
elif income <= 89075:
bracket1 = (10275 * .1)
bracket2 = (41775 - 10275) * .12
bracket3 = (income - 41775) * .22
tax = bracket1 + bracket2 + bracket3
elif income <= 170050:
bracket1 = (10275 * .1)
bracket2 = (41775 - 10275) * .12
bracket3 = (89075 - 41775) * .22
bracket4 = (income - 89075) * .24
tax = bracket1 + bracket2 + bracket3 + bracket4
elif income <= 215950:
bracket1 = (10275 * .1)
bracket2 = (41775 - 10275) * .12
bracket3 = (89075 - 41775) * .22
bracket4 = (170050 - 89075) * .24
bracket5 = (income - 170050) * .32
tax = bracket1 + bracket2 + bracket3 + bracket4 + bracket5
elif income < 539900:
bracket1 = (10275 * .1)
bracket2 = (41775 - 10275) * .12
bracket3 = (89075 - 41775) * .22
bracket4 = (170050 - 89075) * .24
bracket5 = (215950 - 170050) * .32
bracket6 = (income - 215950) * .35
tax = bracket1 + bracket2 + bracket3 + bracket4 + bracket5 + bracket6
else:
bracket1 = (10275 * .1)
bracket2 = (41775 - 10275) * .12
bracket3 = (89075 - 41775) * .22
bracket4 = (170050 - 89075) * .24
bracket5 = (215950 - 170050) * .32
bracket6 = (539900 - 215950) * .35
bracket7 = (income - 539900) * .37
tax = bracket1 + bracket2 + bracket3 + bracket4 + bracket5 + bracket6 + bracket7
print("You owe $" + str(tax) + " on income of $" + str(income) + ". Your tax rate is " + str(tax / income) + "%.")
You owe $110288.35 on income of $390101. Your tax rate is 0.28271742446187015%.
Factoring out common code¶
In the previous example, there was a lot of duplicated code. We can "factor" that code out so it isn't repeated.
income = 390101
tax = 0
bracket1 = (10275 * .1)
bracket2 = (41775 - 10275) * .12
bracket3 = (89075 - 41775) * .22
bracket4 = (170050 - 89075) * .24
bracket5 = (215950 - 170050) * .32
bracket6 = (539900 - 215950) * .35
if income <= 10275:
tax = income * .1
elif income <= 41775:
bracket2 = (income - 10275) * .12
tax = bracket1 + bracket2
elif income <= 89075:
bracket3 = (income - 41775) * .22
tax = bracket1 + bracket2 + bracket3
elif income <= 170050:
bracket4 = (income - 89075) * .24
tax = bracket1 + bracket2 + bracket3 + bracket4
elif income <= 215950:
bracket5 = (income - 170050) * .32
tax = bracket1 + bracket2 + bracket3 + bracket4 + bracket5
elif income < 539900:
bracket6 = (income - 215950) * .35
tax = bracket1 + bracket2 + bracket3 + bracket4 + bracket5 + bracket6
else:
bracket7 = (income - 539900) * .37
tax = bracket1 + bracket2 + bracket3 + bracket4 + bracket5 + bracket6 + bracket7
print("You owe $" + str(tax) + " on income of $" + str(income) + ". Your tax rate is " + str(tax / income) + "%.")
You owe $110288.35 on income of $390101. Your tax rate is 0.28271742446187015%.
One more refinement¶
Key observation: each full bracket always includes all the brackets below it.
income = 390101
bracket1_total = (10275 * .1)
bracket2_total = bracket1_total + (41775 - 10275) * .12
bracket3_total = bracket2_total + (89075 - 41775) * .22
bracket4_total = bracket3_total + (170050 - 89075) * .24
bracket5_total = bracket4_total + (215950 - 170050) * .32
bracket6_total = bracket5_total + (539900 - 215950) * .35
if income <= 10275:
tax = income * .1
elif income <= 41775:
tax = bracket1_total + (income - 10275) * .12
elif income <= 89075:
tax = bracket2_total + (income - 41775) * .22
elif income <= 170050:
tax = bracket3_total + (income - 89075) * .24
elif income <= 215950:
tax = bracket4_total + (income - 170050) * .32
elif income < 539900:
tax = bracket5_total + (income - 215950) * .35
else:
tax = bracket6_total + (income - 539900) * .37
print("You owe $" + str(tax) + " on income of $" + str(income) + ". Your tax rate is " + str(tax / income) + "%.")
You owe $110288.35 on income of $390101. Your tax rate is 0.28271742446187015%.
Decomposition¶
Breaking a problem down into parts or steps is called decomposition.
It's a critical practice in coding!
If you aren't sure where to start, pick the smallest part of the problem you can identify a solution for, and tackle it first.