Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

Python Module 3 – Control flow: decisions, loops and pattern matching

Decisions with if, elif and else

Write if, elif and else chains that cover every case, choose between two values in one line, and avoid the classic x == 1 or 2 mistake in Python.

  • Beginner
  • 18 minutes
  • Examples run with Python 3.14.8 and Pyodide 314.0.7
  • By MySmartCoPilot

What you will learn

  • Write if/elif/else chains that cover every case
  • Use conditional expressions for simple choices
  • Avoid mistakes such as if x == 1 or 2

Before you start

On this page

Almost every useful program has to choose: charge one price or another, accept an answer or ask again, print a warning or stay quiet. Python’s if statement runs a block of code only when a condition is true. Add elif and else and you can describe several cases, of which Python runs exactly one.

The parts of an if statement

An if statement starts with the word if, a condition and a colon. The lines under it that are indented further form its block, and they run only when the condition is true. An else: line at the same indentation as the if starts a second block, for when the condition is false:

An if with an else Python · heat_warning.py
temperature = 41

if temperature > 40:
    print("Heat warning")
    print("Carry water")
else:
    print("A normal day")

print("Forecast done")

Output

Heat warning
Carry water
Forecast done

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 heat_warning.py

The temperature is above 40, so both indented lines under if ran and the else block was skipped. The last line is not indented at all, so it is not part of either block: it runs whatever the temperature is. Change 41 to 25 with Edit and run it again to see the other path.

Three rules make this work:

  • The indentation is the block. Python has no braces or end keywords; a block lasts until a line that is indented less. PEP 8, Python’s style guide, asks for four spaces per level.
  • A block cannot be empty. When you have nothing to put in one yet, write pass, a statement that does nothing, or ..., which people often use the same way.
  • Tabs and spaces do not mix. If some lines are indented with tabs and others with spaces in a way whose meaning depends on how wide a tab is, Python stops with a TabError. Let your editor indent with spaces only.

Forget to indent, and the program does not run at all:

An if without its block Python · missing_block.py
temperature = 41
if temperature > 40:
print("Heat warning")

Output (exit status 1)

Printed as an error (standard error)

  File "missing_block.py", line 3
    print("Heat warning")
    ^^^^^
IndentationError: expected an indented block after 'if' statement on line 2

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 missing_block.py

The message names the line of the if that is missing its block. Like any syntax error, it stops the whole file before the first line runs, so not even the assignment on line 1 happened.

Any value can be a condition

A condition is usually a comparison such as temperature > 40, which is either True or False. But if accepts any value and asks whether it counts as true. False, None, zero (0, 0.0) and empty strings, lists and other empty containers count as false; every other value counts as true. So if name: means “if name is not an empty string”, a short form you will see often. Keep this rule in mind: it explains the classic if mistake shown in the section on x == 1 or 2 below.

Several cases with elif

elif, short for “else if”, adds more conditions between the if and the else. Python tests them from the top, runs the block of the first one that is true, and skips the rest of the chain without even testing it. When no condition is true, the else block runs, if there is one.

Suppose electricity is billed in slabs, where each band of units has its own rate. The function below works out a month’s charge with made-up rates: 3 for each of the first 100 units, 5 for each of the next 200 and 8 for every unit above 300.

def and return

The examples from here on put the decision inside a function, so that the program can try it with several inputs: def energy_charge(units): gives the block a name, and return hands back the result and ends the function at once. Functions get a module of their own later in this track.

Electricity slabs with if, elif and else Python · slab_bill.py
# A month's energy charge, worked out slab by slab: the first 100
# units cost 3 each, the next 200 cost 5 each, and every unit above
# 300 costs 8. The rates are made up for this example.

def energy_charge(units):
    if units <= 100:
        return units * 3
    elif units <= 300:
        return 100 * 3 + (units - 100) * 5
    else:
        return 100 * 3 + 200 * 5 + (units - 300) * 8


print("80 units:", energy_charge(80))
print("100 units:", energy_charge(100))
print("101 units:", energy_charge(101))
print("300 units:", energy_charge(300))
print("301 units:", energy_charge(301))
print("450 units:", energy_charge(450))

Output

80 units: 240
100 units: 300
101 units: 305
300 units: 1300
301 units: 1308
450 units: 2500

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 slab_bill.py

Flowchart of the slab chain: units <= 100 is tested first, then units <= 300, and the else block takes the rest.units <= 100?if blockunits × 3units <= 300?elif block300 +(units − 100) × 5else block1300 +(units − 300) × 8truefalsetruefalse

How the if, elif and else chain of slab_bill.py picks one block

Text description of the diagram

The diagram is a flowchart of energy_charge(units), read from the top.

  1. Python first tests units <= 100. When it is true, the if block runs (units × 3) and nothing else in the chain is tested.
  2. When it is false, Python tests units <= 300. When that is true, the elif block runs: 300 + (units − 100) × 5.
  3. When both tests are false, the else block runs: 1300 + (units − 300) × 8.

Each path ends in exactly one block, so every number of units gets exactly one of the three formulas.

Read the chain the way Python does. For 450 units, units <= 100 is false, units <= 300 is false, so the else block runs. For 250 units the second test is true, and Python never looks at the else. The elif needs no lower limit: it is tested only after units <= 100 was false, so every value that reaches it is already above 100. Written on its own, the same test would be the chained comparison 100 < units <= 300.

The program tries 100 and 101, and 300 and 301, on purpose. Mistakes in chains like this hide at the boundaries, where < and <= give different answers, so test the value on each edge and the one just past it: at 301 units, exactly one unit is charged at the top rate, so the charge is 1300 + 8. And because the chain ends with else, every number of units gets a charge: no case falls through the gaps.

A common mistake: tests in the wrong order

The order of the tests matters as much as the tests themselves. Here the two conditions of the same chain are swapped:

The wider test first Python · wrong_order.py
# The same slabs with the two tests swapped: the wider one is first.

def energy_charge(units):
    if units <= 300:
        return 100 * 3 + (units - 100) * 5
    elif units <= 100:
        return units * 3
    else:
        return 100 * 3 + 200 * 5 + (units - 300) * 8


print("80 units:", energy_charge(80))
print("250 units:", energy_charge(250))

Output

80 units: 200
250 units: 1050

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 wrong_order.py

80 units now cost 200 instead of 240. units <= 300 is also true for 80, so the second slab’s formula runs, and the elif units <= 100 block can never run for any number. Put the narrowest test first, or give every test both of its limits so that the order no longer matters.

Electricity Bill & Appliance Cost Calculator Enter the slabs of this example (up to 100 units at 3, up to 300 at 5, above at 8) and compare its slab-by-slab breakdown with what slab_bill.py prints.

The x == 1 or 2 mistake

Here is a check for weekend prices that looks right and is not:

A condition that is always true Python · weekend_bug.py
day = "Tue"

if day == "Sat" or "Sun":
    print("Weekend price")
else:
    print("Weekday price")

print(day == "Sat" or "Sun")

if day == "Sat" or day == "Sun":
    print("Weekend price")
else:
    print("Weekday price")

if day in ("Sat", "Sun"):
    print("Weekend price")
else:
    print("Weekday price")

Output

Weekend price
Sun
Weekday price
Weekday price

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 weekend_bug.py

On a Tuesday the first if still printed “Weekend price”. Python reads day == "Sat" or "Sun" as (day == "Sat") or "Sun", because == is applied before or. Then or works like this: if its left side counts as true, the result is the left side; otherwise the result is the right side. day == "Sat" is False, so the whole condition is the string "Sun", as the second line of output shows, and a non-empty string counts as true. The if block runs on every day of the week.

The last two tests in the program are the fixes. Either compare the variable each time, day == "Sat" or day == "Sun", or ask whether it is one of several values with in: day in ("Sat", "Sun"). The second form stays short when the list grows. The same trap catches numbers: if x == 1 or 2: is always true, while if x in (1, 2): is what was meant.

A choice in one line: conditional expressions

When all you need is one of two values, a conditional expression does it inside a single line: value_if_true if condition else value_if_false.

Conditional expressions Python · one_line_choice.py
age = 16
label = "adult" if age >= 18 else "minor"
print(age, label)

tickets = 1
print(tickets, "ticket" if tickets == 1 else "tickets")
tickets = 3
print(tickets, "ticket" if tickets == 1 else "tickets")

# Only the chosen side is worked out, so no division happens here.
total = 0
count = 0
average = total / count if count > 0 else 0
print("Average:", average)

Output

16 minor
1 ticket
3 tickets
Average: 0

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 one_line_choice.py

Python works out the condition first and then only the side it chose. That is why the last example is safe: count is 0, so total / count is never calculated and there is no ZeroDivisionError. Use conditional expressions for short choices between two values, like a label or a word that depends on a number. When each case does something, when there are more than two cases, or when you are tempted to put one conditional expression inside another, write an if statement instead: it is easier to read and to change.

Flatter code with guard clauses

Conditions inside conditions push the main work further and further to the right. Both versions of withdraw below give the same answers; compare how they read:

Nested conditions and guard clauses

Nested · withdraw_nested.py

def withdraw(balance, amount):
    if amount > 0:
        if amount <= balance:
            return f"Paid {amount}, {balance - amount} left"
        else:
            return "Not enough money"
    else:
        return "The amount must be more than 0"


print(withdraw(500, 200))
print(withdraw(500, 900))
print(withdraw(500, -5))

Output

Paid 200, 300 left
Not enough money
The amount must be more than 0

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 withdraw_nested.py

Guard clauses · withdraw_guard.py

def withdraw(balance, amount):
    if amount <= 0:
        return "The amount must be more than 0"
    if amount > balance:
        return "Not enough money"
    return f"Paid {amount}, {balance - amount} left"


print(withdraw(500, 200))
print(withdraw(500, 900))
print(withdraw(500, -5))

Output

Paid 200, 300 left
Not enough money
The amount must be more than 0

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 withdraw_guard.py

The second version checks for each problem first and returns straight away. Because return ends the function, the cases that get past both checks need no else, and the normal case sits at the bottom, not indented any further. These early checks are called guard clauses. They keep each rule on its own two lines, so adding a third rule (a daily limit, say) means adding two lines rather than another level of nesting.

Python 3.14: a clearer message for elif after else

An else block must be the last part of its chain. Put an elif after it and Python refuses the file:

elif after else Python · elif_after_else.py
units = 120
if units <= 100:
    print("First slab")
else:
    print("Above 100 units")
elif units <= 300:
    print("Second slab")

Output (exit status 1)

Printed as an error (standard error)

  File "elif_after_else.py", line 6
    elif units <= 300:
    ^^^^
SyntaxError: 'elif' block follows an 'else' block

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 elif_after_else.py

Version note

New in Python 3.14: this mistake gets its own message, 'elif' block follows an 'else' block. Python 3.13 and older report it only as invalid syntax. To fix it, move the else block to the end of the chain.

When you compare one value with many fixed values, such as commands typed by a user, the match statement from a later lesson of this module can be easier to read than a long elif chain.

Python Online Compiler Write your own if, elif and else chains in a full editor and run them in your browser.

Key takeaways

  • An if block is the indented lines after the colon; four spaces per level, never a mix of tabs and spaces. Write pass when a block has nothing to do yet.
  • In an if/elif/else chain Python runs only the first block whose condition is true; finish with else so that every case is covered, and test the values on each boundary.
  • Order the tests from the narrowest to the widest, or give each test both of its limits.
  • x == 1 or 2 is always true; write x == 1 or x == 2, or x in (1, 2).
  • a if condition else b picks one of two values and works out only the side it picks.
  • Guard clauses (check, then return early) keep functions flat and easy to extend.

Exercise

Exercise · Easy · Python

Work out the price of a cinema ticket

A small cinema prices its tickets by age, gives students a discount and charges more at weekends. Write ticket_price(age, is_student, day), which returns the price of one ticket as a whole number.

  • age is a whole number of years, 0 or more; is_student is True or False; day is one of "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" and "Sun".
  • Children under 3 go free: their ticket is 0 on every day.
  • Children from 3 to 12 pay 120.
  • Visitors aged 60 or more pay 160.
  • Students aged 13 to 59 pay 180.
  • Everyone else pays 240.
  • On "Sat" and "Sun", every ticket that is not free costs 40 more.

A student who is also a child or a senior pays the child or senior price, so ticket_price(10, True, "Mon") is 120 and ticket_price(65, True, "Mon") is 160, while ticket_price(25, True, "Sun") is 220.

Starter code · tickets.py

def ticket_price(age, is_student, day):
    """Return the price of one ticket (see the rules in the prompt)."""
    price = 240
    return price
The sample tests · test_tickets.py
from tickets import ticket_price


def test_children():
    """children under 3 go free and children from 3 to 12 pay 120"""
    assert ticket_price(0, False, "Mon") == 0
    assert ticket_price(2, False, "Wed") == 0
    assert ticket_price(3, False, "Mon") == 120
    assert ticket_price(12, False, "Thu") == 120


def test_adults_and_seniors():
    """adults pay 240 and visitors aged 60 or more pay 160"""
    assert ticket_price(13, False, "Mon") == 240
    assert ticket_price(59, False, "Fri") == 240
    assert ticket_price(60, False, "Fri") == 160
    assert ticket_price(85, False, "Tue") == 160


def test_students():
    """students from 13 to 59 pay 180, others the child or senior price"""
    assert ticket_price(13, True, "Mon") == 180
    assert ticket_price(59, True, "Thu") == 180
    assert ticket_price(10, True, "Mon") == 120
    assert ticket_price(65, True, "Mon") == 160


def test_weekend():
    """Saturday and Sunday tickets cost 40 more, except free ones"""
    assert ticket_price(30, False, "Sat") == 280
    assert ticket_price(25, True, "Sun") == 220
    assert ticket_price(8, False, "Sun") == 160
    assert ticket_price(70, False, "Sat") == 200
    assert ticket_price(1, False, "Sat") == 0


def test_weekdays():
    """no weekday costs extra"""
    for day in ("Mon", "Tue", "Wed", "Thu", "Fri"):
        assert ticket_price(30, False, day) == 240
A hint

Handle the free tickets first and return 0 straight away. Then pick the base price with one if/elif/else chain, testing the ages in order (once age <= 12 has been ruled out, the next tests only see visitors of 13 or more), and add the weekend charge afterwards with a separate if day in ("Sat", "Sun"):, so that it is written only once.

The sample tests run on this device, in your browser (Pyodide): nothing is sent to mysmartcopilot.com. The first run downloads Python (about 13.5 MB), which is kept for the next runs. A check in your browser is feedback for you, not proof that the code is right for every input.

Check yourself

5 questions about this lesson. Every answer and why it is right is on the page, behind “Show the answer”. Your score stays in this browser.

  1. Question 1 of 5 What does weekend_bug.py print, with day = "Tue"?

    What does this program print? Choose one answer.

    day = "Tue"
    
    if day == "Sat" or "Sun":
        print("Weekend price")
    else:
        print("Weekday price")
    
    print(day == "Sat" or "Sun")
    
    if day == "Sat" or day == "Sun":
        print("Weekend price")
    else:
        print("Weekday price")
    
    if day in ("Sat", "Sun"):
        print("Weekend price")
    else:
        print("Weekday price")
    Show the answer to question 1

    Answer: it prints

    Weekend price
    Sun
    Weekday price
    Weekday price

    day == "Sat" or "Sun" means (day == "Sat") or "Sun". The comparison is False, so or gives back "Sun", which the second line prints; a non-empty string counts as true, so the first if takes the weekend branch. The two fixed tests compare day itself and print "Weekday price".

  2. Question 2 of 5 In slab_bill.py, which formula does energy_charge(300) use?

    Choose one answer.

    Show the answer to question 2

    Answer: The elif block: 300 + (units − 100) × 5

    300 <= 100 is false, so Python tests the elif: 300 <= 300 is true, so its block runs and the else is skipped. Exactly one block of a chain runs, which is why the output shows 1300 for 300 units.

  3. Question 3 of 5 What does this print?

    Read the code, then choose one answer.

    score = 75
    if score >= 50:
        print("pass")
    elif score >= 70:
        print("merit")
    else:
        print("fail")
    Show the answer to question 3

    Answer: pass

    Python runs the block of the first condition that is true and skips the rest. 75 >= 50 is already true, so "merit" can never be printed by this chain: the wider test comes first. Testing score >= 70 first fixes it.

  4. Question 4 of 5 Which of these conditions are true exactly when day is "Sat" or "Sun"?

    Choose every answer that is right.

    Show the answer to question 4

    Answer:

    • day in ("Sat", "Sun")
    • day == "Sat" or day == "Sun"

    day == "Sat" or day == "Sun" and day in ("Sat", "Sun") both compare day with each of the two values. day == "Sat" or "Sun" is always true, as in the lesson. In day == ("Sat" or "Sun") the brackets come first: "Sat" or "Sun" is just "Sat" (the left side counts as true, so or returns it), so that condition is true only on Saturdays.

  5. Question 5 of 5 What does this print?

    Read the code, then choose one answer.

    count = 0
    average = 90 / count if count > 0 else 0
    print(average)
    Show the answer to question 5

    Answer: 0

    A conditional expression works out its condition first and then only the side it chose. count > 0 is false, so 90 / count is never calculated and average is 0.

References

Related tools

Report a problem with this lesson

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.