Cosi-10a: Introduction to Problem Solving in Python¶

Spring 2025¶

Conditionals¶

Link to interactive slides on Google Colab

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.



No description has been provided for this image

if statements¶

An if statement is the most basic conditional statement in Python. It looks like this:

if <test>:
    statement(s)
In [1]:
x = 4
if x > 5:
    print("This print executes when x is greater than 5")
In [2]:
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 or False
  • 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 to True.

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.

In [3]:
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:

In [4]:
5 > 10
Out[4]:
False
In [5]:
5 != 10
Out[5]:
True
In [6]:
(5 + 1) == 6
Out[6]:
True
In [7]:
(7 * 2) != 14
Out[7]:
False
In [8]:
(6 ** 2) <= 40
Out[8]:
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
In [9]:
(1 > 2) or (5 > 4)
Out[9]:
True
In [10]:
(1 > 2) and (5 > 4)
Out[10]:
False
In [11]:
not (1 > 2)
Out[11]:
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)
In [12]:
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.

In [13]:
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!"?

In [14]:
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:

In [15]:
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.

In [16]:
# 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.

In [17]:
# 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.

In [18]:
# 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:

In [19]:
# 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 ifs 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¶

In [20]:
# 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.

In [21]:
# 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.

In [22]:
# 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¶

In [23]:
# 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¶

In [24]:
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

In [25]:
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

In [26]:
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.

In [27]:
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...¶

In [28]:
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...

In [29]:
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.

In [30]:
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.

In [31]:
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.