Command Palette

Search for a command to run...

Hectal
PHASE 2Beginner ~31 min· topic 1 of 9

Topic 2.1

Making Decisions: if, elif, else

In one line

if runs a block of code only when a condition is True; elif adds more conditions to try in order, and else catches everything left. Python picks at most one branch of an if/elif/else chain, and the indentation shows which lines belong to it.

Think of it like this

Leaving the house. "If it is raining, take an umbrella. Otherwise, wear a cap." You look at the weather, get a yes-or-no answer, and follow one of two plans. A Python if statement works the same way: it asks a yes-or-no question and runs a plan only when the answer is yes.

Words you'll meet

New words in this topic, in plain English. Come back here whenever one feels fuzzy.

Control flow
The order in which a program's lines run, including skipping some and repeating others.
Condition
An expression that gives True or False (or any value Python can treat as one). It decides which block runs.
Block
A group of lines indented the same amount under a line that ends with a colon. It runs (or is skipped) as one unit.
Branch
One of the possible paths through an if/elif/else chain.
elif
Short for "else if": another condition to try when the earlier ones were False.
Truthy / falsy
Whether a value counts as True or False in a condition. 0, "", None and empty collections are falsy.
Nesting
Putting one statement with a block inside another block.
Guard clause
A check at the top that handles a bad or special case first, so the rest of the code stays simple and flat.
pass
A statement that does nothing. It fills a block that Python requires but you have nothing to put in yet.

Step by step

01The simplest decision: if

Here is one if. Python works out (evaluates) temperature > 30. If the answer is True, the indented line runs. If it is False, Python skips the indented line and carries on below it.

Notice the two rules: the line ends with a colon, and the next line is indented (four spaces is the standard).

main.pywhole filepython
temperature = 35

if temperature > 30:
    print("It is hot. Drink water.")

print("This line always runs.")

02The block is defined by indentation

In many languages curly brackets mark a block. In Python the indentation itself is the grammar (Topic 0.6). Every line indented under the if belongs to it. The first line that goes back to the left margin is outside it and always runs.

This is why a line that is indented by mistake changes the meaning of the program: it moves from "always" to "only when True".

main.pywhole filepython
age = 10

if age >= 18:
    print("You can vote.")
    print("Remember your ID.")     # also inside the block
print("Goodbye.")                  # outside: always runs

03else: the other plan

else runs when the if condition was False. It sits at the same indentation as its if, and ends with a colon but has no condition.

The condition here uses and (Topic 1.8): it is True only when both sides are True. With age = 15 and a ticket, both are True, so the first block runs.

Exactly one of the two blocks runs each time.

main.pywhole filepython
age = 15
has_ticket = True

if age >= 12 and has_ticket:
    print("Enter the cinema")
else:
    print("Stay outside")

04elif: many choices, first match wins

Python tests score >= 90 first. If that is False it tests score >= 75, and so on. The moment one condition is True, its block runs and the rest of the chain is skipped. The final else runs only if nothing matched.

For score = 72 the first two tests fail and the third passes, so the grade is "C". Try it in the first runnable example below.

main.pywhole filepython
score = 72

if score >= 90:
    grade = "A"
elif score >= 75:
    grade = "B"
elif score >= 60:
    grade = "C"
else:
    grade = "F"

print("Grade:", grade)
elif: many choices, first match winsdiagram
Rendering diagram…

05Order matters, and elif is not many ifs

With score = 95, the chain below prints only "Passed", because the first True condition wins and the better branch after it is never reached. That is a bug in the order: the specific test (>= 90) must come before the general one (>= 60).

If you wrote three separate if statements instead of elif, each would be asked on its own, and for 95 all three blocks could run. Choose elif when the options exclude each other.

main.pywhole filepython
score = 95

# Wrong order: the general test comes first
if score >= 60:
    print("Passed")
elif score >= 90:
    print("Distinction")      # never reached

# Separate ifs: each one is its own question
if score >= 60:
    print("Passed")
if score >= 90:
    print("Distinction")      # both lines print

06Any value can be a condition

Python converts the condition to True or False using the truthiness rules from Topic 1.4. An empty string, 0 and None are false. So you can test "is there anything here?" directly.

A common mistake is to write if name == None: or if flag == True:. The short forms if name is None: (when you mean exactly None) and if flag: are the standard style.

main.pywhole filepython
nickname = ""

if nickname:
    print("Hello,", nickname)
else:
    print("No nickname given")     # empty text is falsy

07Flat is better than nested

The nested version below is correct, but the real work ("Valid") is buried two levels deep and the two error messages are far from the tests that cause them. The flat version tests the bad cases first, one elif each, and the good case is the final else.

Both give the same result. Flat code is easier to read, change and test.

main.pywhole filepython
age = -5
name = "Mia"

# Nested
if age >= 0:
    if name != "":
        print("Valid:", name, age)
    else:
        print("Name is empty")
else:
    print("Age cannot be negative")

# Flat: reject bad cases first
if age < 0:
    print("Age cannot be negative")
elif name == "":
    print("Name is empty")
else:
    print("Valid:", name, age)

08pass: a block that does nothing yet

Python does not allow an empty block: a colon must be followed by at least one indented statement. When you want a branch that deliberately does nothing (or is still a to-do), write pass.

It is a placeholder, not a comment: it is a real statement that does nothing when run.

main.pywhole filepython
stock = 0

if stock == 0:
    pass            # TODO: decide what to do when out of stock
else:
    print("In stock")

Try it yourself

  1. 1

    Predict, then run

    In the first example set score = 75. Before you press Run, write down the grade you expect. Is 75 greater than or equal to 75? Then set it to 74 and predict again. Values right at the edge of a rule (75 and 74 here) are where mistakes hide, so always test them.

  2. 2

    Reorder the chain

    Move the elif score >= 60 branch above the elif score >= 75 branch and run with score = 80. You will get "C" instead of "B". The code is valid; only the order changed, and with it the meaning.

  3. 3

    Turn elif into separate ifs

    In the last part of the first example, change the elif to if and run it. Now both lines print. Say in one sentence why.

Code & diagrams

Grades with if, elif and else New tab

Change score to 95, 60 and 12 and predict the grade before you run. The last part shows that the first True branch wins even when later ones are also True.

Sign in to run this example in your browser.

Expected output

Score: 72
Grade: C
Enter the cinema
Passed (first match wins)
Flat checks, pass and truthiness New tab

Set age to 20 and run again to see the "Valid" line. Then give nickname a value and watch the last branch change.

Sign in to run this example in your browser.

Expected output

Age cannot be negative
Age cannot be negative
Done
Empty text counts as False
How an if/elif/else chain flowsdiagram
Rendering diagram…

Break it on purpose

Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.

Break #1

Forget the colon

Delete the colon at the end of the if line and run it.

main.pywhole filepython
age = 15
if age >= 12
    print("ok")
terminal
$ python main.py
── what you'll see ──
File "main.py", line 2
if age >= 12
^
SyntaxError: expected ':'

Break #2

Use = instead of ==

Change == to a single = in a condition.

main.pywhole filepython
x = 5
if x = 5:
    print("five")
terminal
$ python main.py
── what you'll see ──
File "main.py", line 2
if x = 5:
^^^^^
SyntaxError: invalid syntax. Maybe you meant '==' or ':=' instead of '='?

Break #3

Forget to indent the block

Put the print on the same indentation as the if.

main.pywhole filepython
age = 15
if age >= 12:
print("ok")
terminal
$ python main.py
── what you'll see ──
File "main.py", line 3
print("ok")
^^^^^
IndentationError: expected an indented block after 'if' statement on line 2

Myth vs fact

Myth

elif and a second if are the same thing.

Fact

After elif, Python skips the rest of the chain once one branch ran. A new if starts a fresh question, so several blocks can run. The two give different results whenever more than one condition is True.

Myth

A condition must be True or False.

Fact

Any value works. Python decides using truthiness: 0, 0.0, "", None, [], {} and set() are falsy, and almost everything else is truthy.

Myth

Every if needs an else.

Fact

No. An if alone means "do this only when true, otherwise carry on". Add else only when the other case needs its own action.

Myth

Python checks all the elif conditions to be safe.

Fact

It stops at the first True condition. Later conditions are not even evaluated, so an expensive or risky test can safely sit lower in the chain.

Pro corner

Extra depth for experienced readers. New to this? Skip it for now and come back later.

  • ▸

    Conditions are evaluated lazily and left to right with and/or short-circuiting (Topic 1.8). That lets you write guards like if items and items[0] > 5:: when the list is empty the second part never runs, so there is no IndexError.

  • ▸

    CPython compiles an if/elif chain into a sequence of conditional jumps in the bytecode (Topic 13.3). Long chains test one condition after another, so for a chain of many exact-value tests a dictionary lookup (one hash lookup, whatever the size) or match (Topic 2.8) can be clearer. Note that match is not a jump table: CPython still tests the cases one after another, so choose it for readability, not speed.

  • ▸

    Truthiness has traps beyond empty values. A user-defined class is truthy unless it defines __bool__ or __len__ (Topic 8.7). A NumPy array or a pandas Series with more than one element raises ValueError: The truth value of an array ... is ambiguous in an if, so write if arr.any(): or if len(arr): there. Also if x == True: is False for x = 5 even though 5 is truthy (only 1 equals True), and if x is True: accepts only the real True.

  • ▸

    Python has no switch statement from C or Java. For many equal-value choices, people use an elif chain, a dictionary that maps values to actions, or match (3.10 and later).

  • ▸

    Validation code is a classic place for guard clauses. When we reach functions you will write if value is None: return ... at the top, so the "normal" code is never indented inside a pile of else blocks. That habit also makes the code easier to test, since each early return is one case.

Remember this

  1. 1

    The question is called a condition. It is any piece of code that gives a value (Python calls that an expression) and whose answer is True or False, such as age >= 18 or name == "Asha" (comparisons from Topic 1.8). The line if age >= 18: ends with a colon, and the lines after it are indented by four spaces. That indented group is the block: it runs only when the condition is True, and is skipped completely when it is False.

  2. 2

    else is the "otherwise" plan. It has no condition of its own, because it means "everything the if did not catch". An if/else pair always runs exactly one of its two blocks, never both and never neither.

  3. 3

    elif (short for "else if") lets you ask more questions in a row. Python checks the conditions from top to bottom and runs the block of the first one that is True. It then jumps past the whole chain without even looking at the rest. So order matters: put the most specific question first. A score of 95 satisfies score >= 60 too, so if you ask that first, the "distinction" branch is never reached.

  4. 4

    Separate if statements are not the same as an if/elif chain. Each separate if is its own question, and more than one block can run. Use elif when the choices exclude each other (a grade is A or B or C). Use separate ifs when the facts are independent (you can have a ticket and be hungry).

  5. 5

    Any value can be a condition, not only True and False. Python treats 0, 0.0, "" (empty text), None (the "nothing here" value) and empty collections (such as an empty list [], taught in Phase 4) as false (falsy), and nearly everything else as true (truthy), as you saw in Topic 1.4. So if nickname: reads naturally as "if there is a nickname". Do not write if x == True:. Write if x:.

  6. 6

    Deep nesting (an if inside an if inside an if) pushes the useful code far to the right and is hard to read. Prefer a flat chain that rejects bad cases first. This idea is called a guard clause. Once you know functions (Topic 5.10), you will use it with an early return, which leaves a function at once.

  7. 7

    One more small tool: when a branch must exist but has nothing to do yet, write pass, Python's "do nothing" statement.

Explain it without notes

01

How does Python decide which branch of an if/elif/else chain to run?

02

What is the difference between a chain of elifs and several separate if statements?

03

Which values are falsy in Python, and why is if items: better than if len(items) > 0:?

04

What is a guard clause and why do programmers prefer a flat chain over deep nesting?

Practice

01

Set n = 7. Print Positive if it is above 0, Negative if below 0 and Zero otherwise.

02

Set year = 2024. Print Leap year if the year is divisible by 4 but not by 100, or is divisible by 400. Otherwise print Not a leap year.

03

Set hour = 14. Print Good morning before 12, Good afternoon from 12 to 17, and Good evening from 18 on.

Trade-offs

  • ↔

    Flat chains vs nested ifs. Flat is easier to read and test; nesting is fine for two levels when the inner decision truly belongs to one outer case.

  • ↔

    Truthiness shortcuts vs explicit checks. if items: is short and idiomatic, but it hides the difference between an empty list and None. Be explicit (is None, == 0) when the difference matters.

  • ↔

    An elif chain vs a dictionary or match. A chain of a few conditions is the clearest. For many fixed values a dictionary lookup or match (Topic 2.8) can be tidier.

Done when you can

  • Done when you can write an if/elif/else chain with correct colons and indentation.

  • Done when you can explain why order matters in an elif chain and fix a chain that is in the wrong order.

  • Done when you can say when to use elif and when separate if statements.

  • Done when you can list the falsy values and use truthiness in a condition.

  • Done when you can rewrite a nested decision as a flat chain and use pass for an empty block.