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,"",Noneand 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).
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".
age = 10
if age >= 18:
print("You can vote.")
print("Remember your ID.") # also inside the block
print("Goodbye.") # outside: always runs03else: 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.
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.
score = 72
if score >= 90:
grade = "A"
elif score >= 75:
grade = "B"
elif score >= 60:
grade = "C"
else:
grade = "F"
print("Grade:", grade)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.
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 print06Any 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.
nickname = ""
if nickname:
print("Hello,", nickname)
else:
print("No nickname given") # empty text is falsy07Flat 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.
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.
stock = 0
if stock == 0:
pass # TODO: decide what to do when out of stock
else:
print("In stock")Try it yourself
- 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
Reorder the chain
Move the
elif score >= 60branch above theelif score >= 75branch and run withscore = 80. You will get "C" instead of "B". The code is valid; only the order changed, and with it the meaning. - 3
Turn elif into separate ifs
In the last part of the first example, change the
eliftoifand run it. Now both lines print. Say in one sentence why.
Code & diagrams
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.
Expected output
Score: 72
Grade: C
Enter the cinema
Passed (first match wins)Set age to 20 and run again to see the "Valid" line. Then give nickname a value and watch the last branch change.
Expected output
Age cannot be negative
Age cannot be negative
Done
Empty text counts as FalseBreak 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.
age = 15
if age >= 12
print("ok")Break #2
Use = instead of ==
Change == to a single = in a condition.
x = 5
if x = 5:
print("five")Break #3
Forget to indent the block
Put the print on the same indentation as the if.
age = 15
if age >= 12:
print("ok")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/orshort-circuiting (Topic 1.8). That lets you write guards likeif items and items[0] > 5:: when the list is empty the second part never runs, so there is noIndexError. - ▸
CPython compiles an
if/elifchain 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) ormatch(Topic 2.8) can be clearer. Note thatmatchis 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 raisesValueError: The truth value of an array ... is ambiguousin anif, so writeif arr.any():orif len(arr):there. Alsoif x == True:is False forx = 5even though 5 is truthy (only1equalsTrue), andif x is True:accepts only the realTrue. - ▸
Python has no
switchstatement from C or Java. For many equal-value choices, people use anelifchain, a dictionary that maps values to actions, ormatch(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 ofelseblocks. That habit also makes the code easier to test, since each early return is one case.
Remember this
- 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
TrueorFalse, such asage >= 18orname == "Asha"(comparisons from Topic 1.8). The lineif 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
elseis the "otherwise" plan. It has no condition of its own, because it means "everything theifdid not catch". Anif/elsepair always runs exactly one of its two blocks, never both and never neither. - 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 satisfiesscore >= 60too, so if you ask that first, the "distinction" branch is never reached. - 4
Separate
ifstatements are not the same as anif/elifchain. Each separateifis its own question, and more than one block can run. Useelifwhen the choices exclude each other (a grade is A or B or C). Use separateifs when the facts are independent (you can have a ticket and be hungry). - 5
Any value can be a condition, not only
TrueandFalse. Python treats0,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. Soif nickname:reads naturally as "if there is a nickname". Do not writeif x == True:. Writeif x:. - 6
Deep nesting (an
ifinside anifinside anif) 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 earlyreturn, which leaves a function at once. - 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
How does Python decide which branch of an if/elif/else chain to run?
What is the difference between a chain of elifs and several separate if statements?
Which values are falsy in Python, and why is if items: better than if len(items) > 0:?
What is a guard clause and why do programmers prefer a flat chain over deep nesting?
Practice
Set n = 7. Print Positive if it is above 0, Negative if below 0 and Zero otherwise.
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.
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 andNone. 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
elifand when separateifstatements.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
passfor an empty block.