Pebellslearn by building
0%
RoadmapMatrixCatalogSandboxSign in
Fundamentals  ›  Lesson

Conditionals

Conditionals 16 minMaking decisions with if / elif / else — and match / case
You're building a piece ofBill Splitter & Tip Calculator
This piece — apply_discount(): Applies codes like SAVE10 / HALF to the bill.
Scenario A student in the group enters the code STUDENT at checkout, so the app must knock 15% off the bill.
Your task
Two ways to choose. 1) Build apply_discount(bill, code) with if/elif/else — it returns the discounted bill (SAVE10 10% off, STUDENT 15%, HALF 50%, else none). 2) Build discount_rate(code) with match/case — it returns the rate itself (0.1, 0.15, 0.5, or 0.0). Examples: apply_discount(100, "SAVE10") → 90.0 · discount_rate("SAVE10") → 0.1.

Conditionals

Conditionals let your program choose what to do. The shape is if → optional elifs → optional else:

temp = 30
if temp > 25:
    print("hot")
elif temp > 15:
    print("mild")
else:
    print("cold")

Python checks each condition top to bottom and runs the first block whose condition is True. The rest are skipped. Remember: the block is defined by indentation (4 spaces).

Comparison operators

These produce a bool (True/False):

a == b     # equal          (two equals signs! one '=' is assignment)
a != b     # not equal
a <  b     # less than
a <= b     # less than or equal
a >  b     # greater than
a >= b     # greater than or equal

= vs == is the classic beginner bug. x = 5 assigns 5 to x. x == 5 asks "is x equal to 5?". Conditions always use ==.

❓ Cross-question — "Where's ===? Is == loose like JavaScript's?" Neither exists here. Python's == compares values with no type coercion (1 == "1" is False, never a silent match). "Same object" is is — the rough equivalent of JS === on objects. Rule: use == for values, and is only for None.

Combining conditions: and / or / not

Three tiny words combine True/False values:

  • andTrue only if both sides are true
  • orTrue if at least one side is true
  • notflips it (not True is False)
python

In a real condition each side is usually a comparison:

python

Chaining many conditions with parentheses

You can combine as many as you like and use parentheses to group them — read an expression from the inside out, just like math. Take this one:

python

It runs the if branch. Here's why, step by step — run this to watch each piece collapse to a single True/False:

python
  • and needs every value to be true — one False makes the whole and false (that's why right is False).
  • or needs just one true value — so left or right is True, and the if runs.

Precedence: with no parentheses, not runs first, then and, then or. So True or True and False is True (the and binds tighter). When in doubt, add parentheses — they cost nothing and make your intent obvious.

Try it: in the big condition above, flip some Trues to False and Run. Can you land on the else branch? (You'll have to make both the left group and the right group false.)

Truthiness

Any value can be tested directly. These count as False: False, 0, 0.0, "" (empty string), [] (empty list), None. Everything else is True:

name = ""
if name:                 # empty string is falsy
    print("hi", name)
else:
    print("no name given")

The list is worth memorising, because one entry catches nearly everyone:

What does this print?
if "0":
    print("truthy")
else:
    print("falsy")

❓ Cross-question — "Are empty collections falsy like in JS?" Careful — more things are falsy here. In JS [] and {} are truthy; in Python [], {}, "", 0, and None are all falsy. So if items: cleanly reads as "is the list non-empty?" — a very common Python idiom that surprises JS devs.

The pattern: map an input to a result

Here's the same shape applied to shipping costs — study how it's built, then you'll write your own for discount codes:

def shipping_cost(country):
    if country == "US":
        return 5.00
    elif country == "CA":
        return 8.00
    else:
        return 15.00        # everywhere else

Notice how return inside an if exits the function immediately — once a branch returns, none of the others run. That's exactly the structure you need for the discount codes below: compare the code, return the right price, and fall back to else for unknown codes.

Python's switch: match / case

Many languages have a switch statement for picking one option out of several. Python's version is match / case. When you're comparing one value against a set of fixed options, it can read more cleanly than a long if/elif chain:

def shipping_cost(country):
    match country:
        case "US":
            return 5.00
        case "CA":
            return 8.00
        case _:               # _ is the catch-all — it works like `else`
            return 15.00

Read it as: "match country against these cases, run the first that fits, and case _ catches everything else." It does the same job as the if/elif/else version above — pick whichever reads more clearly.

You can even match several values in one case with | (read it as "or"):

match code:
    case "SAVE10" | "SUMMER":    # either code → 10% off
        return 0.10
    case "HALF":
        return 0.50
    case _:
        return 0.0

⚠️ Version note: match / case needs Python 3.10 or newer. This playground runs Python 3.14 (via the project's .venv), so it works here — you'll use it in Task 2 below. The one gotcha: if you ever run the app on Python 3.9, match raises a SyntaxError.

❓ Cross-question — "Is match just a switch? Does it fall through?" No fall-through — only the first matching case runs, no break needed. And it's more powerful than a C/JS switch: it destructures tuples, lists, and dicts (structural pattern matching) — closer to handling a TypeScript discriminated union than a plain value switch.


Idioms & real-world patterns

You know if/elif/else and match. Here are the shorter, sharper ways Python developers actually write decisions every day.

The ternary — an if that returns a value

When you just need to pick between two values, the one-line conditional expression (the ternary) beats a four-line if:

python

Read it left-to-right: "big if bill > 50 else small."

Coming from Java/JS: the parts are re-ordered. cond ? a : b becomes a if cond else b — the value comes first, the condition in the middle. No ? or :.

x or default — fill in a missing value

or returns the first truthy operand (not just True/False), so it's the Python way to supply a fallback:

python

and mirrors it (returns the first falsy operand). Both short-circuit — the right side is skipped once the left decides the answer.

Coming from Java/JS: same short-circuit as || / &&, but they return the operand, not a boolean. Mind the falsy trap: count or 10 replaces a real 0 with 10. When 0 is valid, use count if count is not None else 10.

❓ Cross-question — "Is x or default the same as TypeScript's x ?? default?" Close, but not identical. TS's ?? triggers only on null/undefined; Python's or triggers on any falsy value (0, "", []). For a true None-only fallback, write x if x is not None else default. Python has no ?. or ??.

Chained comparisons — low < x < high

Python chains comparisons the way maths does — no and needed:

python

Coming from Java/JS: 0 <= x <= 100 is a syntax error there — you'd write x >= 0 && x <= 100. In Python it's one expression, and x is evaluated once.

Guard clauses — return early, stop nesting

Check the bad cases first and return, instead of wrapping the happy path in ever-deeper ifs. Flat code reads better:

python

any() and all() — one question over a whole list

any(iterable) is True if at least one item is truthy; all(iterable) is True only if every item is. They replace a manual loop-with-a-flag:

python

Real-world — validate an order form with guards + all:

python

⚡ Advanced — the walrus := and richer match

The walrus operator := assigns and returns a value at once — handy when you'd otherwise compute something twice:

python

match also destructures sequences and dicts, and takes a case ... if guard:

python

🎯 Your turn

Two functions, the same decision written two ways. Check runs the tests for both — implement each with the style named.

Task 1 — apply_discount(bill, code) with if / elif / else. Returns the bill after the code, rounded to two decimals. Codes: SAVE10 10% off · STUDENT 15% off · HALF 50% off · anything else no discount.

  • apply_discount(100, "SAVE10")90.0
  • apply_discount(100, "")100.0

Task 2 — discount_rate(code) with match / case. Returns the rate itself (a number) — a case per code, and case _ for the rest.

  • discount_rate("SAVE10")0.1
  • discount_rate("NOPE")0.0

Hint — Task 1 chains if/elif/else (a 10% discount means you pay 90%, bill * 0.90). Task 2 is match code: with a case "SAVE10": per code and a final case _: catch-all.

Then press ▶ Run, tap the Live App try chips (there's a card for each function), and hit ✓ Check. All green = you've done it both ways. ✅

Practice — level up

0/5 solved

Solve each in its own editor — Run to try, Check to grade. Stuck? Reveal a Hint, or the full Solution + walkthrough.

Warm-upSign of a number

Write sign(n) returning "positive", "negative" or "zero".

conditionals
DrillClamp into a range

Write clamp(n, lo, hi) that returns n limited to the range lo..hi — below lo gives lo, above hi gives hi. Use a ternary.

ternarychained-comparison
BuildCheckout status

Write checkout_status(total, coupon, is_member).

  • total <= 0"empty cart"
  • Free shipping if the customer is a member OR total >= 50, else $5.
  • Discount: coupon "VIP" and member → 20% off; coupon "SAVE10" → 10% off; else none.
  • Return f"pay $<final>, shipping <FREE|$5>" with final to 2 decimals.
guardsand/orternaryf-strings
BossPassword strength

Write password_strength(pw) scoring these 5 rules — length ≥ 8, has a digit, an uppercase, a lowercase, a symbol (non-alphanumeric). Return "strong" (≥4 rules), "ok" (≥2), else "weak".

any/allstring-methodsconditionals
CapstoneMini request router

Write route_request(method, path, is_admin) using match on (method, path):

  • GET /"home"; GET /health"ok"
  • GET /admin"admin panel" if is_admin else "403 forbidden"
  • POST /login"logging in"
  • anything else → f"404 <method> <path>"
matchguardsf-strings
apply_discount.py
Call the function you wrote — just like your app's frontend would. Edit the inputs and hit Call.
apply_discount(bill, code) → floatReturn the bill after the discount code is applied.
try:
returned
discount_rate(code) → floatReturn the discount rate for a code, written with match/case.
try:
returned
Press Run to execute your code.
Press Check to run the tests for this lesson.
Conditionals — Pebells