Level 1 · First Words

Making Decisions 🔀

This is the lesson where your programs stop being a straight line and start being a map with branches. It is also where Python's most famous quirk shows up.

if

grog = 8

if grog > 5:
    print("That is enough grog.")
    print("Genuinely, that is plenty.")

print("This line always runs, branch or no branch.")
That is enough grog.
Genuinely, that is plenty.
This line always runs, branch or no branch.

Two pieces of punctuation are doing all the work:

Indentation is not decoration. It is the syntax.

Most languages mark blocks with curly braces and treat layout as a matter of taste. Python has no braces: the layout is the structure. This is the single thing newcomers find strangest, and the thing they defend most fiercely after six months.

ready = True

if ready:
    print("inside the if")
    print("also inside")
print("outside again")
inside the if
also inside
outside again
ENCYCLOPEDIA[Medium: Success]

The reasoning is worth knowing. In brace languages, programmers indent anyway, for humans. So there are two structures in every file: the one the compiler reads (braces) and the one the human reads (indentation). When they disagree, the human is misled. Apple's 2014 'goto fail' security bug was exactly this: a stray indented line that looked like it was inside an if and was not. Python makes them the same structure so they cannot disagree.

⚠️ Four spaces. Never tabs. Never mixed.

Python accepts either, but mixing them in one file gives TabError: inconsistent use of tabs and spaces, and the two look identical on screen, which makes it maddening. Set your editor to insert four spaces when you press Tab and forget the problem exists. In VS Code that is the default. PEP 8 says four spaces, so the whole world agrees on this one.

if / else

password = "grog"

if password == "swordfish":
    print("Access granted.")
else:
    print("Access denied. You fight like a dairy farmer.")
Access denied. You fight like a dairy farmer.

if / elif / else: choosing from many

score = 87

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

print(f"Score {score} earns a {grade}.")
Score 87 earns a B.

elif is short for "else if". Python checks each condition in order and stops at the first true one. That is why score >= 80 works without also checking score < 90: if we reached that line at all, the first test must have failed.

Order matters enormously. This version is broken:

score = 95

if score >= 60:
    grade = "D"          # always wins, because it is checked first
elif score >= 90:
    grade = "A"          # unreachable
else:
    grade = "F"

print(f"Score {score} earns a {grade}. Which is wrong.")
Score 95 earns a D. Which is wrong.

Nesting

logged_in = True
is_admin = False

if logged_in:
    print("Welcome back.")
    if is_admin:
        print("Admin console available.")
    else:
        print("Standard account.")
else:
    print("Please sign in.")
Welcome back.
Standard account.

Each level of nesting is another four spaces. It works, and past two levels deep it becomes hard to read. When you find yourself at three or four, that is a signal to restructure, usually by pulling a piece out into a function (Lesson 17) or by returning early.

The one-line version

age = 20
status = "adult" if age >= 18 else "minor"
print(status)

crew = []
print(f"Crew: {len(crew) if crew else 'nobody yet'}")
adult
Crew: nobody yet

This is a conditional expression, and reads value-first: "adult, if age is 18 or more, otherwise minor". Lovely for short choices, unreadable for long ones. If it does not fit comfortably on one line, use a normal if.

match: the modern multi-way branch

Python 3.10 added match, which is tidier when you are comparing one value against many possibilities:

command = "north"

match command:
    case "north" | "n":
        print("You walk north. The jungle thickens.")
    case "south" | "s":
        print("You walk south. The beach is behind you.")
    case "look":
        print("Trees. So many trees.")
    case _:
        print(f"I do not know how to '{command}'.")
You walk north. The jungle thickens.

The _ case is the catch-all, like else. match can do far more than this (it can pull apart lists and objects), and Lesson 33 returns to it. For simple choices, either style is fine; match shines when there are many.

Exercise 1

The bouncer

Write a program that decides entry. Under 18: refused. 18 to 20: allowed but no alcohol. 21 and over: allowed. Print a different message for each.

Reveal solution
age = 19

if age < 18:
    print("Sorry, come back in a few years.")
elif age < 21:
    print("You are in, but soft drinks only.")
else:
    print("Enjoy your evening.")
You are in, but soft drinks only.

Notice the second condition is just age < 21, with no need for age >= 18 and age < 21. Reaching that line already proves the first test failed.

Exercise 2

FizzBuzz, the famous one

For the number 15: print Fizz if it divides by 3, Buzz if by 5, FizzBuzz if by both, and the number otherwise. Careful with the order.

Reveal solution
n = 15

if n % 3 == 0 and n % 5 == 0:
    print("FizzBuzz")
elif n % 3 == 0:
    print("Fizz")
elif n % 5 == 0:
    print("Buzz")
else:
    print(n)
FizzBuzz

The both-case must come first. Put it last and 15 matches n % 3 == 0 and prints Fizz, and you have written the single most common wrong answer to the single most famous interview question. Lesson 9 makes it print 1 to 100.

Exercise 3

Find the indentation bug

This should only congratulate winners. What does it actually do, and why?

score = 20

if score > 100:
    print("New high score!")
print("Congratulations!")
Reveal solution

It always congratulates. The second print is not indented, so it is not inside the if at all; it is the next statement in the program.

score = 20

if score > 100:
    print("New high score!")
    print("Congratulations!")
else:
    print("Not this time.")
Not this time.

In a brace language this bug hides. In Python it is visible the moment you look at the shape of the code, which is the entire argument for the design.

+100 XP