PYTHON / GETTING STARTED
Python syntax and significant indentation
Read and write Python blocks correctly by using indentation, colons, and bracket continuation as the language's actual grammar.
What you will learn
- Open a block with a colon and indent every statement of that block to the same column
- Recognise IndentationError from a missing block versus an unexpected extra indent
- Continue a long expression across lines inside (), [] or {} without indentation rules
- Close a block by dedenting back to the column of an enclosing statement
Understanding Python syntax and significant indentation
Most languages mark the start and end of a block with braces and treat whitespace as decoration. Python's tokenizer does the opposite: it measures the leading whitespace of every logical line and emits INDENT and DEDENT tokens from the changes. Those tokens are real grammar symbols, so indentation is not a formatting preference here, it is the syntax that says where a block begins and ends.
A compound statement such as if, for, while, def or class has a header line that ends in a colon, followed by a suite: one or more lines indented deeper than the header. Every statement in that same suite must start at exactly the same column, because the tokenizer only compares each line's indentation with the stack of enclosing levels. Indenting a sibling statement further does not create a nested block; there is no header and no colon to open one, so it is an error. Going back out to a shallower column closes the suite, and that column must match a level already on the stack.
Indentation only matters between logical lines. Once the tokenizer is inside an unclosed (, [ or {, newlines and leading whitespace are ignored until the bracket closes, which is why a long list or a multi-line arithmetic expression can be laid out freely. Four spaces per level is the community convention, and Python 3 refuses to guess when tabs and spaces are mixed ambiguously in the same block, raising TabError rather than silently picking an interpretation.
def classify(n):
if n < 0:
label = "negative"
else:
label = "non-negative"
return label
for value in (-2, 0, 3):
print(value, classify(value))
print("done")
Leading whitespace is tokenised into INDENT and DEDENT symbols, so indentation is Python's block syntax rather than a style choice.
Worked examples
One space changes what runs
Shows how the indentation column alone decides whether a statement is inside the loop or after it.
total = 0
for n in [1, 2, 3]:
total += n
print("inside", total)
print("after", total)
Example explained
Line 1total += n and print("inside", total) sit at the same column, so both belong to the loop body and run on every iteration.
Line 2The final print is at column 0, which dedents out of the loop, so it runs once after the loop finishes.
Line 3Moving that last line four spaces to the right would print 'after' three times without any other change to the code.
Brackets suspend the indentation rules
Demonstrates that inside unclosed brackets, line breaks and leading whitespace carry no meaning.
numbers = [
1, 2,
3,
]
message = ("a "
"b")
total = (numbers[0]
+ numbers[2])
print(numbers, message, total)
Example explained
Line 1The list spans four physical lines but is one logical line, so the odd 8-space indent before 3 is accepted.
Line 2Two string literals separated only by whitespace are concatenated at compile time, giving "a b".
Line 3The + at the start of a continuation line is legal because the expression is still inside the parentheses.
Line 4print receives three arguments and joins them with a single space.
The two indentation errors, side by side
Compiles small snippets at runtime to show which layouts Python rejects and which it accepts.
snippets = [
"if True:\nprint('x')\n",
"x = 1\n y = 2\n",
"if True:\n pass\n",
]
for src in snippets:
try:
exec(src)
print("compiled")
except IndentationError as e:
print(type(e).__name__)
Example explained
Line 1The first snippet ends a header with a colon but never indents, so no suite exists for the if statement.
Line 2The second indents a line that no header opened, which is the 'unexpected indent' form of the same error class.
Line 3The third is valid: pass is a real statement that satisfies the grammar's need for a non-empty suite.
Line 4Both failures are raised while compiling the string, before any of it executes.
Important notes
A short suite may be written on the header line, as in `if n < 0: return 0`, which is valid syntax but cannot contain another compound statement.
Blank lines and comment lines carry no indentation meaning inside a block, so their leading whitespace never triggers an error.
Common mistakes
Indenting a follow-up statement deeper than its sibling to 'group' it visually: Python reports IndentationError: unexpected indent because only a colon header can open a new level.
Writing a header with a colon and then leaving the body empty as a placeholder, which fails to compile; use pass or Ellipsis as the body.
Copying code from a page that uses tabs into a file that uses spaces, producing a TabError or, worse, a block that compiles but runs the wrong lines together.
Try it yourself
Change, predict, then run
Write a loop over [4, 9, 16] that prints each number and, inside an if, prints "big" when the number exceeds 10; then move the if statement out of the loop body and note how the printed lines change.
Open the Python workspaceCheck your understanding
A for loop body has its first statement indented 4 spaces and its second statement indented 8 spaces. What does Python do?
- It runs fine; Python only requires each body line to be indented more than the header.
- It runs fine, and the second statement becomes a nested block inside the first.
- It raises IndentationError, because every statement in one suite must begin at the same column.
- It raises TabError, because changing the indentation width counts as mixing tabs and spaces.
Show answer
The extra indent produces an INDENT token, but a new block can only be opened by a header line ending in a colon, so the tokenizer reports an unexpected indent. Option 1 is tempting because the first body line only needs to be deeper than the header, but once that level is established the rest of the suite is pinned to exactly that column.