Chapter 09

Making decisions — if, elif, else

Taking different paths depending on a condition, why indentation carries meaning in Python, and the silent bug that comes from ordering conditions wrongly.

35 minPython 3.12
  1. 1Encounter
  2. 2Understand
  3. 3Worked
  4. 4Predict
  5. 5Apply
  6. 6Stretch

The problem we are solving

In the last mission we learned to ask questions — is age >= 18 true or false? But knowing the answer, the program did nothing with it; it only printed True or False.

Now it is time to act on that answer. One path if the condition holds, another if it does not. This is the moment where code stops being a list of steps and starts making decisions.

And here Python differs from many other languages: which lines are inside a condition and which are outside is decided by whitespace. There are no curly braces. It feels strange at first and then you start to like it — because code that looks organised is organised.

By the end of this mission you can

  • Run code conditionally with if
  • Provide an alternative with else, and several with elif
  • Indent correctly, and read and fix an IndentationError
  • Explain why the order of conditions matters — the biggest trap here
  • Nest a condition inside another, and recognise when to avoid it

Prerequisites: True and False — learning to ask questions.


The simplest form

python
age = 22

if age >= 18:
    print("You may enter")

print("Program finished")
text
You may enter
Program finished

The shape has three parts:

  1. The word if, then a condition
  2. A colon : at the end of the condition — leaving it out is the most common mistake
  3. The lines that follow, indented by four spaces — those indented lines are what the condition governs

Had age been 15, the first print would not have run, but the last one still would — because it is outside the condition.

Indentation is the meaning

These two programs have almost the same characters and behave completely differently:

python
score = 30

if score >= 50:
    print("Passed")
    print("Certificate issued")

Run this and nothing appears in the terminal — both lines are inside the condition, and the condition is false.

python
score = 30

if score >= 50:
    print("Passed")
print("Certificate issued")
text
Certificate issued

The second print is now outside the condition, so it always runs — and a certificate has just been issued to somebody who failed. One line indented wrongly breaks the business rule, and Python raises no error, because both versions are valid code.

Four spaces, not tabs. Python accepts either, but mixing them in one file gives a TabError, and it is painful to find because tabs and spaces look identical on screen. In VS Code, pressing Tab inserts four spaces — that is the setting everybody uses, and four spaces is Python's official convention.

else — the other path

python
age = 15

if age >= 18:
    print("You may enter")
else:
    print("Sorry, adults only")
text
Sorry, adults only

else carries no condition, because its condition has already been stated — "if none of the above was true". Exactly one of the if and the else runs; never both, never neither.

elif — several paths

python
marks = 72

if marks >= 80:
    grade = "A"
elif marks >= 70:
    grade = "B"
elif marks >= 60:
    grade = "C"
elif marks >= 40:
    grade = "D"
else:
    grade = "F"

print(f"Marks {marks} gives grade {grade}")
text
Marks 72 gives grade B

elif means "otherwise, if". What happens is this: Python works down the chain, runs the body of the first condition it finds true, and then leaves the whole chain. It does not even test the rest.

With marks = 72: 72 >= 80 is false, 72 >= 70 is true → grade = "B", done. 72 >= 60 and 72 >= 40 are also true, but they are never looked at.

And here is the biggest trap

Because elif stops at the first true condition, getting the order wrong produces a program that runs, raises nothing, and answers incorrectly:

python
marks = 95

if marks >= 40:
    grade = "Pass"
elif marks >= 80:
    grade = "Excellent"
else:
    grade = "Fail"

print(grade)
text
Pass

95 marks and no Excellent. Because 95 >= 40 was true immediately, and Python stopped there — the second condition was never tested at all.

The rule to remember: put the strictest, most specific condition first and the loosest last. When working with numeric bands, walk downwards from the largest, as in the grade example above.

This bug is so quiet that seeing it is the only way to catch it. So make a habit of it: after writing a chain of conditions, run it once with a value for each branch. The grade code above should give five different answers for 95, 72, 65, 45 and 20. If any two agree, the order is wrong.

A condition inside a condition

python
is_logged_in = True
is_admin = False

if is_logged_in:
    if is_admin:
        print("Welcome, admin")
    else:
        print("Welcome, user")
else:
    print("Please log in")
text
Welcome, user

The inner if is indented eight spaces, because it sits under the outer one. This works, but the more levels there are, the harder the code is to read. Often and says the same thing more clearly:

python
is_logged_in = True
is_admin = False

if is_logged_in and is_admin:
    print("Welcome, admin")
elif is_logged_in:
    print("Welcome, user")
else:
    print("Please log in")
text
Welcome, user

Which is better depends on which reads more easily. When you find yourself more than three levels deep, stop and rethink — it is almost always a sign the conditions can be arranged differently.

Validating input

We now have the tool that solves the problem left hanging in an earlier mission — what to do when someone types nothing, or types a word:

python
raw = input("Your age: ").strip()

if raw == "":
    print("You did not type anything")
elif raw.isdigit():
    age = int(raw)
    if age >= 18:
        print(f"Age {age}: you may enter")
    else:
        print(f"Age {age}: adults only")
else:
    print(f"'{raw}' is not a whole number")
text
Your age: twenty
'twenty' is not a whole number

isdigit() is a new method — it reports whether every character in the text is a digit. It is the simple way to check before handing anything to int(), and it makes ValueError go away. (isdigit() does not accept negatives or decimals — those need try, which comes later.)


A complete example

fare.py:

python
# Work out a ticket fare from age and day of week
age = int(input("Passenger age : "))
day = input("Day of week   : ").strip().lower()

base_fare = 120.0

# Most specific rule first, loosest last
if age < 5:
    fare = 0.0
    category = "Infant"
elif age < 18:
    fare = base_fare * 0.5
    category = "Child"
elif age >= 60:
    fare = base_fare * 0.7
    category = "Senior"
else:
    fare = base_fare
    category = "Adult"

is_weekend = day == "saturday" or day == "sunday"
if is_weekend:
    fare = fare * 1.2

print()
print(f"Category : {category}")
print(f"Weekend  : {is_weekend}")
print(f"Fare     : {fare:.2f}")
text
Passenger age : 65
Day of week   : Sunday

Category : Senior
Weekend  : True
Fare     : 100.80

Notice two design decisions. First, the age conditions are chained with elif, because a passenger falls into exactly one category. Second, the weekend condition is a separate if, because it is an independent question — whatever the age, the weekend surcharge applies on top. Deciding what is an elif and what is a separate if is the real work of writing conditions.


When it breaks

SyntaxError: expected ':' The colon at the end of the if, elif or else line is missing.

IndentationError: expected an indented block after 'if' statement The line after the if was not indented. A condition needs at least one line inside it.

IndentationError: unexpected indent Some line is indented for no reason. Work out which block that line is supposed to belong to.

TabError: inconsistent use of tabs and spaces in indentation Tabs and spaces are mixed in one file. Run "convert indentation to spaces" in your editor.

SyntaxError — an elif or else standing alone elif and else always belong to an if, and must be indented to the same level as that if.

TypeError: '>=' not supported between instances of 'str' and 'int' You forgot to convert the input(). Write int(input(...)).

The conditions look right but take the wrong path Check the order. With a loose condition first, the strict one is never tested. Run it once with a value for each branch.