AmouAI Hub/Courses/Programming Fundamentals/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.
By the end of today you can
- Write
if,if/elseandif/elif/elsecorrectly, with proper indentation - Explain why the order of
elifbranches changes the result - Translate an English business rule into a boolean expression
- Flatten nested conditionals using
and,orand guard clauses - Apply De Morgan's laws to simplify an ugly condition
- 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.
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.
temperature = 31
if temperature > 30:
print("It is hot.")
print("Drink water.")
print("This always runs.")Three things to notice:
- The condition is any expression that produces
TrueorFalse. - The line ends with a colon. Forgetting it is the single most common syntax error of the week.
- The indented lines are the body. They run only if the condition is true. The unindented
line after them is outside the
ifand always runs.
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
age = 15
if age >= 18:
print("You may vote.")
else:
print("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.
score = 85
if score >= 90:
grade = "A"
elif score >= 80:
grade = "B"
elif score >= 70:
grade = "C"
else:
grade = "F"
print(grade)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
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")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.
| Requirement | Condition |
|---|---|
| Free shipping on orders over $50 | total > 50 |
| ...unless the item is oversized | total > 50 and not oversized |
| Members get it regardless of total | (total > 50 and not oversized) or is_member |
| Teenagers are 13 to 19 inclusive | 13 <= age <= 19 |
| The password is valid if it is 8+ characters and has a digit | len(pw) >= 8 and has_digit |
| Closed on weekends | day == "Sat" or day == "Sun" |
| Neither cash nor card accepted | not (cash or card) |
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.
| Member | Total > 50 | Oversized | Free shipping? |
|---|---|---|---|
| yes | — | — | yes |
| no | yes | no | yes |
| no | yes | yes | no |
| no | no | — | no |
Now the code writes itself, and you can point at the table when someone asks whether you handled a case.
De Morgan's laws
Two identities that let you push a not inward and usually make a condition readable:
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":
# 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()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:
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)Correct, and genuinely hard to check. The same rule, flattened:
year = 2024
is_leap = year % 4 == 0 and (year % 100 != 0 or year % 400 == 0)
print(is_leap)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:
# 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:
# 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)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
# 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 mistake | What happens | Fix |
|---|---|---|---|
| 1 | if x = 5: | SyntaxError — Python protects you here | if x == 5: |
| 2 | if x == 1 or 2: | Always true. No error. | if x in (1, 2): |
| 3 | if price == 0.3: | False even when it should be true (floats) | if abs(price - 0.3) < 1e-9: |
| 4 | if is_ready == True: | Works, but breaks for non-bool truthy values | if is_ready: |
| 5 | Missing colon | SyntaxError: expected ':' | Add the colon |
| 6 | Mixed tabs and spaces | TabError or silently wrong branches | Spaces only, 4 per level |
| 7 | Broadest elif first | Later branches unreachable. No error. | Order narrowest to broadest |
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
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}")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.
input(), comma separated →
✓Exercise set
Checked automatically the moment you submit. Work top to bottom — each one assumes the last. Your answers are saved in this browser.
Day 4 — Iteration: Loops
Doing something many times without writing it many times.