Day 3 / 25 Conditional Logic & Control Flow 0/0 exercises Exercises ↓

AmouAI Hub/Courses/Programming Fundamentals/Day 3

Week 1 · Python Core Foundations · Day 3

Conditional Logic & Control Flow

Until now your programs ran straight through, top to bottom, doing the same thing every time. Today they start making decisions - and the hard part is not the syntax, it is saying precisely what you mean.

Study time
4 hours + exercises
Reading
Think Python, Ch. 5
Focus
Branching, boolean design

By the end of today you can

  1. Write if, if/else and if/elif/else correctly, with proper indentation
  2. Explain why the order of elif branches changes the result
  3. Translate an English business rule into a boolean expression
  4. Flatten nested conditionals using and, or and guard clauses
  5. Apply De Morgan's laws to simplify an ugly condition
  6. Recognise the seven classic conditional bugs on sight

Today's videos

Watch each video, then work the matching sections below. Watching alone will not do it.

3A
Making Decisions (120 min)
Boolean expressions as the engine of control flow · if / elif / else · indentation as syntax · why branch order matters · nesting and flattening · chained comparisons · the ternary · a look at match.
3B
Logic Design & Common Traps (120 min)
English to boolean, eight worked translations · decision tables · De Morgan's laws · guard clauses and early exit · the seven classic traps · flowchart to code · two full programs.

1The if statement

One new idea, one new piece of punctuation, and one rule about whitespace that Python takes more seriously than any other language you will meet.

first_if.py
temperature = 31

if temperature > 30:
    print("It is hot.")
    print("Drink water.")

print("This always runs.")
It is hot. Drink water. This always runs.

Three things to notice:

  1. The condition is any expression that produces True or False.
  2. The line ends with a colon. Forgetting it is the single most common syntax error of the week.
  3. The indented lines are the body. They run only if the condition is true. The unindented line after them is outside the if and always runs.
In Python, indentation is syntax

In most languages indentation is a courtesy and braces do the real work. In Python the indentation is the structure. Four spaces per level, consistently. Do not mix tabs and spaces - your editor should be set to insert spaces when you press Tab, and every mainstream editor does this by default.

if / else

if_else.py
age = 15

if age >= 18:
    print("You may vote.")
else:
    print("Not yet.")
Not yet.

if / elif / else

Python checks each condition in order and runs the body of the first one that is true. Then it skips the rest entirely.

grades.py
score = 85

if score >= 90:
    grade = "A"
elif score >= 80:
    grade = "B"
elif score >= 70:
    grade = "C"
else:
    grade = "F"

print(grade)
B
Branch tracerinteractive

Drag the score. Watch which condition is tested, which one wins, and which branches Python never even looks at.

85
Order matters more than you expect

Put score >= 60 first and everybody gets a D, because 95 is also greater than 60 and Python stops at the first match. Tick the "broken order" box in the tracer above and drag the slider - watch every score collapse into one grade.

elif versus a stack of separate ifs

elif_vs_if.py
n = 15

# elif: at most ONE branch runs
if n > 10:
    print("more than 10")
elif n > 5:
    print("more than 5")

print("---")

# separate ifs: EVERY true condition runs
if n > 10:
    print("more than 10")
if n > 5:
    print("more than 5")
more than 10 --- more than 10 more than 5

Use elif when the cases are alternatives. Use separate ifs when the checks are independent and several can apply at once.

2From English to boolean

This is the real skill of the day. Syntax takes twenty minutes to learn. Saying exactly what you mean takes practice.

The method: write the rule as a sentence. Underline every condition. Decide whether the connectors are "and" (all must hold) or "or" (any will do). Then translate word by word.

RequirementCondition
Free shipping on orders over $50total > 50
...unless the item is oversizedtotal > 50 and not oversized
Members get it regardless of total(total > 50 and not oversized) or is_member
Teenagers are 13 to 19 inclusive13 <= age <= 19
The password is valid if it is 8+ characters and has a digitlen(pw) >= 8 and has_digit
Closed on weekendsday == "Sat" or day == "Sun"
Neither cash nor card acceptednot (cash or card)
if x == 1 or 2 does not do what it looks like

Python reads it as (x == 1) or (2), and 2 is truthy, so the whole thing is always true. It never errors - it just silently misbehaves. Write x == 1 or x == 2, or better, x in (1, 2).

Decision tables

When a rule has three or more inputs, stop guessing at nested ifs and draw the table first.

MemberTotal > 50OversizedFree shipping?
yesyes
noyesnoyes
noyesyesno
nonono

Now the code writes itself, and you can point at the table when someone asks whether you handled a case.

Condition evaluatorlive python

Set the values, write a condition using them, and see what Python decides. Good for testing a rule before you build it into a program.

python not loaded
Press Evaluate.

De Morgan's laws

Two identities that let you push a not inward and usually make a condition readable:

demorgan.py
not (a and b)   ==   (not a) or  (not b)
not (a or  b)   ==   (not a) and (not b)

Worked example. "Reject the order if it is not the case that the customer is verified and the address is valid":

demorgan_use.py
# straight translation - hard to read aloud
if not (is_verified and address_valid):
    reject()

# De Morgan - reads exactly like the business rule
if not is_verified or not address_valid:
    reject()
Equivalence checkerlive python

Enter two conditions using a and b. Every combination is tested. If they ever disagree, you get the counterexample.

vs
Press Compare.

Try a pair that is not equivalent: not (a and b) vs not a and not b

3Nesting, flattening and guard clauses

Nested conditionals are sometimes necessary and usually avoidable. Knowing which is which is a craft skill.

Here is the leap year rule, written the obvious nested way:

leap_nested.py
year = 2024

if year % 4 == 0:
    if year % 100 == 0:
        if year % 400 == 0:
            is_leap = True
        else:
            is_leap = False
    else:
        is_leap = True
else:
    is_leap = False

print(is_leap)
True

Correct, and genuinely hard to check. The same rule, flattened:

leap_flat.py
year = 2024
is_leap = year % 4 == 0 and (year % 100 != 0 or year % 400 == 0)
print(is_leap)
True

Read it aloud: "divisible by four, and either not a century year or divisible by four hundred." That is the actual rule. The flat version is the specification.

Guard clauses

When you are validating input, deep nesting appears almost immediately:

nested_validation.py
# the arrow of doom
if username:
    if len(username) >= 3:
        if username.isalnum():
            print("Welcome,", username)
        else:
            print("Letters and digits only")
    else:
        print("Too short")
else:
    print("Username required")

Handle each failure immediately and get it out of the way. The happy path ends up unindented at the bottom, where it is easy to find:

guards.py
# guard clauses - each problem dealt with and dismissed
if not username:
    print("Username required")
elif len(username) < 3:
    print("Too short")
elif not username.isalnum():
    print("Letters and digits only")
else:
    print("Welcome,", username)
Flat is better than nested

That is a line from the Zen of Python (import this). Every level of indentation is another thing the reader has to hold in their head. Two levels is normal. Four is a smell.

The conditional expression

ternary.py
# instead of four lines
if score >= 50:
    status = "pass"
else:
    status = "fail"

# one line
status = "pass" if score >= 50 else "fail"

Use it when the whole thing fits comfortably on one line and reads like English. Do not nest them.

4The seven classic traps

Each of these has cost every programmer alive at least one afternoon.

#The mistakeWhat happensFix
1if x = 5:SyntaxError — Python protects you hereif x == 5:
2if x == 1 or 2:Always true. No error.if x in (1, 2):
3if price == 0.3:False even when it should be true (floats)if abs(price - 0.3) < 1e-9:
4if is_ready == True:Works, but breaks for non-bool truthy valuesif is_ready:
5Missing colonSyntaxError: expected ':'Add the colon
6Mixed tabs and spacesTabError or silently wrong branchesSpaces only, 4 per level
7Broadest elif firstLater branches unreachable. No error.Order narrowest to broadest
Traps 2 and 7 are the dangerous ones

The others crash, and a crash tells you where to look. These two produce a program that runs perfectly and gives the wrong answer - which you may not notice until someone else does.

A complete worked program

shipping_cost.py
# shipping_cost.py
BASE_RATE = 8.00
OVERSIZE_SURCHARGE = 15.00
FREE_THRESHOLD = 50.00

order_total = 62.50
is_member = False
is_oversized = True
destination = "domestic"

# Decide the surcharge first - it applies either way
surcharge = OVERSIZE_SURCHARGE if is_oversized else 0.00

# Then decide the base shipping
if is_member:
    shipping = 0.00
    reason = "member benefit"
elif order_total >= FREE_THRESHOLD and not is_oversized:
    shipping = 0.00
    reason = "free over $50"
elif destination == "international":
    shipping = BASE_RATE * 3
    reason = "international rate"
else:
    shipping = BASE_RATE
    reason = "standard rate"

total = order_total + shipping + surcharge

print(f"Order:      ${order_total:>7.2f}")
print(f"Shipping:   ${shipping:>7.2f}   ({reason})")
print(f"Surcharge:  ${surcharge:>7.2f}")
print(f"Total:      ${total:>7.2f}")
Order: $ 62.50 Shipping: $ 8.00 (standard rate) Surcharge: $ 15.00 Total: $ 85.50

Notice the structure: one decision that is genuinely independent is computed separately with a ternary; the mutually exclusive cases go in one ordered elif chain, narrowest first; and every branch sets both the number and a reason, so the output can explain itself.

>_Python playground

A real Python interpreter running inside your browser. Nothing is installed, nothing is uploaded, nothing can break.

scratch.pypython not loaded
Values for input(), comma separated →
Output appears here. The first run takes a few seconds while Python loads.

Exercise set

Checked automatically the moment you submit. Work top to bottom — each one assumes the last. Your answers are saved in this browser.

All Warm-up Core Challenge Reset day

Day 4 — Iteration: Loops

Doing something many times without writing it many times.

Continue →