Did you know that moving a single Python line four spaces to the left can change how many times it runs? A beginner may understand an if condition perfectly and still get the wrong output because the print statement belongs to the wrong block. In Python, indentation is not just a way of making code neat. It can determine the program’s meaning.
A Python indentation block is a suite of statements grouped at a particular indentation level under a header such as if, else or for. The immediate rule is to align else: with the if it belongs to, indent each branch’s statements one level deeper, and dedent a statement when it should run after that block. A correctly indented line can run inside a condition, once per loop iteration or only after the whole loop, depending on where it sits.
This is a small programming problem with a large educational reward. It teaches students to slow down, predict control flow, check the first and last cases, and prove why a line executes instead of using a screen’s output as a substitute for reasoning. It also gives parents and teachers a concrete, gentle way to diagnose mistakes without simply telling the learner to add more spaces.
A two-minute diagnostic before teaching
Show learners a four-line conditional and ask them to draw a vertical line beside the statements that belong together. Begin with the question: ‘Will the final message print only when the condition is true, or no matter what?’ If a learner answers from the English meaning of the message rather than the indentation, the lesson should focus on block boundaries.
score = 72
if score >= 70:
print('Ready for a challenge')
print('Check complete')
The sample is invented code for illustration, not actual student grading data or a prescription for educational placements. Because 72 is at least 70, both messages print. But print('Check complete') is aligned with the if, not indented inside it. If score were 62, the challenge message would be skipped while ‘Check complete’ would still appear. The difference comes from indentation, not from the words in the message.
Why Python treats spaces as part of the structure
In many programming languages, braces mark the beginning and end of a code block. Python commonly uses indentation together with the header’s colon. A line ending in : introduces a suite; statements in that suite are indented relative to the header. When indentation returns to the header’s level, the suite has ended. That is why a line of apparently harmless spacing is part of the logic.
For if and else, the two headers are peers: else: is written at the same indentation as its corresponding if. Their child statements are one level deeper. At runtime Python tests the if condition. If it is true, the if suite executes and the else suite does not; otherwise the else suite executes, if one is provided.
The Python language reference for compound statements explains clauses and suites precisely. Python’s official control-flow tutorial provides the familiar if, elif and else forms. These are the correct references for language behaviour rather than an invented rule about how Python ‘usually’ works.
Hand-checked example: three illustrative study decisions
Invented data for a practice program: a learner writes a tiny script to label three pretend quiz marks 52, 84 and 67 using a numerical threshold of 70. This fictional rule is only for tracing code. It does not describe an MOE or SEAB grading rule and should not be used to allocate real students to learning groups.
marks = [52, 84, 67]
for mark in marks:
if mark >= 70:
print('Stretch task')
else:
print('Foundation task')
print('One mark checked')
print('All marks checked')
First, identify the indentation levels. The for header starts at the left margin. Both the nested if and its matching else begin four spaces farther right. Each branch’s message is eight spaces in. print('One mark checked') returns to the four-space level, so it belongs to the loop but is outside the if/else choice. print('All marks checked') returns to the left margin, so it runs only after the loop has finished.
Trace the first value: mark = 52
Check 52 ≥ 70. It is false, so Python skips ‘Stretch task’ and prints ‘Foundation task’. Then the branch ends. The statement ‘One mark checked’ runs because it is still within the loop but not restricted to the true branch. That gives two output lines for the first mark.
Trace the second value: mark = 84
Check 84 ≥ 70. It is true, so Python prints ‘Stretch task’ and skips ‘Foundation task’. It then prints ‘One mark checked’. Again there are two lines for this iteration. The threshold comparison is independent of the indentation decision, so students should check both separately.
Trace the third value: mark = 67
Check 67 ≥ 70. It is false, so the script prints ‘Foundation task’, then ‘One mark checked’. The loop has now visited all three marks. Only after that does the final unindented statement print ‘All marks checked’.
The exact output, checked line by line
Foundation task
One mark checked
Stretch task
One mark checked
Foundation task
One mark checked
All marks checked
Count the output independently: each of the three marks produces one branch message and one per-iteration message, or 3 × 2 = 6 lines. The final summary adds one more, making 7 lines. The output is not just a prediction; it is a statement about how the code’s blocks divide work among branches and iterations.
The quiet bug: a valid program with the wrong behaviour
Consider a student who means to print ‘One mark checked’ after every mark, but accidentally moves that line all the way to the left margin. The program may still be syntactically valid. It simply does something different.
marks = [52, 84, 67]
for mark in marks:
if mark >= 70:
print('Stretch task')
else:
print('Foundation task')
print('One mark checked')
This revised script prints a branch message for each of the three marks, then prints ‘One mark checked’ once when the loop ends. Its output consists of four lines rather than six or seven. No exception is required for this to be a genuine bug: the code may run, but its behaviour fails the learner’s original intention. That is why ‘it ran without an error’ is not the same as ‘it is correct’.
The repair is to move the final print statement back inside the loop at the four-space level, if the intention is to acknowledge every mark. The learner should not move the line eight spaces in, either, because then it would belong to only one of the branches. The correct location depends on the intended execution frequency.
A second subtle mistake: the else belongs to the wrong header
If else: is not aligned with the nested if, Python may reject the indentation, or it may recognise an entirely different construct. Python permits an else associated with a for or while loop. A loop’s else is not the ‘otherwise’ branch of an if; its behaviour depends on whether the loop finishes without a break. This is a real language feature, not a teaching metaphor.
for mark in [52, 84]:
if mark >= 70:
print('High')
else:
print('Loop completed')
Here else: is aligned with for, not with the inner if. The code prints ‘High’ for 84 and then ‘Loop completed’ once because the loop finishes normally without a break. It does not print ‘Loop completed’ as an alternative for the 52 mark. Beginners need not master loop-else immediately, but they should learn to inspect which header owns an else before assuming what it means.
A useful mental model: draw blocks, then trace one line at a time
- Read each line that ends with a colon as a potential block header.
- Mark the indentation column of that header and the deeper column used for its suite.
- Match
else:to the intendedifat the same indentation level. - Follow a single sample value through the comparison and only the selected branch.
- Check which lines run regardless of the branch but still inside an enclosing loop.
- Check which lines run after the loop ends, then count outputs across all iterations.
- Predict the result on paper before executing the code; use actual output to test, not replace, the reasoning.
You can use a pencil and a vertical guide down the page. For many children, outlining the block boundaries is more effective than immediately typing faster. When code is copied from a chat message or PDF, invisible spacing differences can become especially important. A plain-text code editor with visible indentation guides can help learners inspect the actual structure.
Four spaces, tabs and the difference between convention and correctness
Python’s widely used PEP 8 style guide recommends four spaces per indentation level. This is a style convention that improves consistency; the language does not universally require exactly four spaces. Consistent indentation is what creates properly recognised block structure. Mixed tabs and spaces can create confusing alignment and may cause a TabError when their interpretation is inconsistent.
In beginner lessons, choosing four spaces consistently is sensible. A student who memorises ‘always press Tab once’ without checking whether the editor inserts a tab character or spaces may accidentally create code that looks aligned on one screen but behaves differently or raises an error. Configure the editor deliberately and make invisible whitespace visible when debugging.
Progressive practice, from guided to independent
Guided: predict two cases
Give the class x = 3, followed by if x > 5: and an indented line printing ‘Large’, then an aligned else: with an indented line printing ‘Small’, followed by an unindented line printing ‘Done’. Ask for the exact output when x is 3 and when x is 9. For 3: ‘Small’, then ‘Done’. For 9: ‘Large’, then ‘Done’. In both cases, the unindented line runs after the branch.
Paired: repair a message that prints only once
Write a loop that prints a learning prompt for every one of three fictional items but has its ‘item checked’ statement outside the loop. One student draws the block structure; the other predicts the original output and proposes a one-level indentation repair. They swap roles and test with a different number of items. The goal is to justify why the line moved, not simply to imitate a screenshot.
Independent: count before running
Ask the learner to trace four pretend scores [70, 69, 70, 71] with a condition mark >= 70, two alternative branch messages, one line inside the loop after the branch, and one line after the loop. The learner should find that three scores meet the inclusive threshold (70, 70 and 71), one does not (69), four per-iteration messages print, and one final message prints. The total is 4 branch + 4 per-item + 1 final = 9 lines. The exercise tests the inclusive boundary and block scope together.
Transfer: change only one indentation level
Now move the per-iteration message to the left margin and ask for the new line count without changing the data. The branch count remains four, but the message prints once at the end; the final message also prints once. The result is 4 + 1 + 1 = 6 lines. This shows students that structural changes can alter output frequency even when conditions and input values stay the same.
A compact learner error log
- Prediction error: write what you expected and which line you misunderstood.
- Block error: circle the header and identify the statement’s true indentation level.
- Condition error: check the test itself, including whether a threshold uses
>or>=. - Output error: recount the expected number of lines and test with one item before testing with several.
- Repair check: change only the necessary line, rerun both a true and a false case, and confirm no new mistake appears.
A teacher can assess progress without expensive software: provide a short code sample and ask a student to narrate what runs once, what runs each time, and what runs only if a test succeeds. Parents can use the same questions on paper. A learner who can explain a changed result after a single dedent is demonstrating transferable control-flow understanding.
Limitations: do not treat every issue as an indentation mistake
Some Python errors arise from a missing colon, an undefined variable, a type mismatch, a wrong comparison operator or a mistaken loop range. Indentation is only one possible mechanism. The diagnosis should match the observed error, and the code should be tested in an appropriate Python environment. If a program has side effects such as changing files, sending messages or modifying real school data, use a harmless illustrative version before experimenting.
Actual teachers also choose thresholds and tasks using educational judgement, not a toy mark of 70. All marks and messages in this article are fictional programming data. Correct code is not a guarantee of better grades; the educational progress is the learner’s increased ability to reason about and check an algorithm.
Frequently asked questions
Is indentation optional in Python?
No. Indentation determines how suites are grouped. The exact number of spaces is a consistency and style choice within the language’s rules, but the structural indentation itself matters.
Does else always belong to the closest if on the previous line?
No. Indentation determines which statement the clause attaches to. An aligned else might pair with a particular if, or with a loop if it is aligned with for or while. Read the structural levels, not the visual closeness alone.
Why does my last print run even when if is false?
If the print is outside the if suite, it is not controlled by that condition. It may still be inside an enclosing loop; draw both boundaries.
What happens if a line has inconsistent indentation?
Depending on the exact code, Python may reject it with an indentation-related error, or it may interpret a valid but unintended block. Correct the structural relationship and use consistent spaces.
Is elif just an extra else?
elif introduces an additional conditional test in the same decision chain. Python evaluates conditions in order, and the first matching suite is selected; an optional final else handles the case when none matches.
Verified learning routes and technical references
Read how to avoid off-by-one errors in Python range() loops for another kind of control-flow diagnostic. The G3 Computing subject guide provides wider course context. Python’s official compound-statement reference and PEP 8 distinguish language rules from formatting advice.
The eduKate Education Hub links broader study pathways. The immutable Clementi Mathematics tutorial reference illustrates the educational sequence of spotting the specific misconception, explaining why the rule works, practising progressively and checking independently.
The core aim: make your reasoning executable
Learning Python indentation is not mainly about making lines look tidy. It is learning to say precisely which action belongs to which condition and when each action should run. Once students can predict that sequence, test it and correct the exact failing block, they are thinking independently about programs rather than merely copying them.