Most languages use curly braces or begin/end to mark where a block of code starts and ends. Python doesn't. It uses the indentation itself as part of the grammar, which means whitespace at the start of a line is meaningful to the parser, not just to a human reader. This lesson covers how Python reads indentation, what the rules are, the two errors that come from getting it wrong, and the habits that keep code clean.
Compare the same if statement in two languages:
JavaScript uses the curly braces to mark the body of the if. The indentation is purely cosmetic. The same code written on a single line still works. Python uses the indentation itself. There are no braces. The two lines that belong to the if are the ones indented underneath it, and that's how Python knows.
Guido van Rossum, who designed Python, made this choice on purpose. Programmers were already indenting their code to make it readable, then writing braces on top of that to satisfy the compiler. Python skips the braces and uses the indentation. The visible structure and the parsed structure are the same, so code can't look correct while parsing wrong.
The trade-off is strictness. If indentation is inconsistent, Python refuses to run the file.
Python's parser walks through source line by line and tracks the indentation level of each statement, measured in columns from the left margin. When indentation increases, the parser interprets that as the start of a new block. When it decreases, the block ends.
Python's lexer emits two invisible tokens, called INDENT and DEDENT, every time the indentation level changes. They behave like the opening and closing braces in other languages, generated from whitespace.
Consider this function:
The parser sees it as:
| Line | Content | Indentation |
|---|---|---|
| 1 | def cart_total(prices): | 0 |
| 2 | total = 0 | 4 (INDENT into function body) |
| 3 | for price in prices: | 4 |
| 4 | total += price | 8 (INDENT into loop body) |
| 5 | return total | 4 (DEDENT out of loop body) |
Two new blocks open, two blocks close. The parser builds the same tree it would if explicit braces were written, but it builds it from the column counts.
The same structure as a diagram:
The function body holds three statements at indent level 4. The loop body holds one statement at indent level 8. Every line inside the same block must share the exact same leading whitespace, character for character.
Indentation tokens are generated at parse time, so the runtime cost is zero. The strictness is at parse time: inconsistent indentation stops the file from running.
The language allows either spaces or tabs for indentation. PEP 8, the official style guide, says use four spaces per level. The Python standard library uses four spaces, popular editors default to four spaces, and code reviews assume it.
Tabs and spaces look identical on screen but are different bytes underneath. A tab might render as 4 spaces in one editor, 8 in another, and something else in a code review tool. If a file mixes both, the visual indentation no longer matches what the parser sees, and the code can look correct while parsing wrong.
Use spaces consistently.
Most editors are set up to insert four spaces when the Tab key is pressed. Enabling "show whitespace" or "show invisibles" in the editor's view menu confirms this: tabs appear as arrows, spaces as dots.
Python 3 made one change over Python 2: it refuses to mix tabs and spaces in a way the parser would have to guess about. A file that uses a tab on one line and four spaces on the next at the same logical indent level raises TabError and stops. The old behavior, where the interpreter picked an interpretation without warning, caused too many bugs.
IndentationError: When the Whitespace Doesn't Line UpIndentationError is raised when a line is indented in a way Python can't make sense of. It's a special case of SyntaxError, but Python gives it a distinct name because the cause is specific.
Missing indentation after a block opener:
A def, if, for, while, class, try, or any other line that ends in a colon requires a block of code on the following lines. Python expects the next non-empty line to be indented further than the line with the colon. If it isn't, this error is raised.
Unexpected extra indentation:
The first line sits at column 0, so the parser is at indent level 0. The second line jumps to column 4 with no block opener above it (no if, def, or similar). Python can't determine which block this line belongs to, so it stops.
Inconsistent indentation inside a block:
The first statement inside the function indented by 4 spaces. The next jumped to 6. Inside one block, every statement has to start at the same column. Pick a width and use it consistently for that block.
The decision the parser is making is small and mechanical, which is why these errors are easy to fix: pick the column where the block starts, and make sure every statement inside the block lines up at that column.
TabError: When You Mix Tabs and SpacesTabError is a subclass of IndentationError. It's raised when the same block uses a mix of tabs and spaces in a way that's ambiguous.
The first line of the body uses four spaces. The second line uses a tab. They look the same when rendered, but they aren't the same bytes.
The fix is to convert every line in the file to use only spaces. Most editors have a "convert indentation to spaces" command for this case. Once converted, the file runs.
Useful prevention settings in any editor:
These three settings together prevent introducing a tab where a space was intended.
The same indentation rules apply to every kind of block in Python. The line that ends with a colon is a block header. The lines indented beneath it are the block body. The same pattern applies across the constructs covered in the rest of this course.
`if` / `elif` / `else`:
Each branch is its own block. The body of if doesn't have to be the same length as the body of elif, but each body has to be indented consistently within itself.
`for` and `while` loops:
The two statements inside the loop are indented by 4 spaces. The final print returns to indent level 0, which ends the loop. The result is that the last line runs once, not three times.
`def` for functions:
`class` for classes:
The class example shows nesting. The method __init__ is indented one level inside the class. The body of __init__ is indented one more level. Each INDENT corresponds to a deeper block.
`try` / `except`:
Same rule again: each clause's body is indented under its header.
Nesting works by adding one more indentation level for each block that's inside another. Code typically stays at two or three levels deep. Past that, the indentation itself signals that the code is doing too much in one place.
A function that filters in-stock products from a small catalog:
The function body is indent 4. The for body is indent 8. The outer if body is indent 12. The inner if body is indent 16. Four levels deep, and the content drifts away from the left margin.
Flatten by combining conditions:
Now the deepest indent is 12, and the code is easier to read. The PEP 8 line length limit (79 characters) combined with deep nesting often forces refactoring like this, which is part of why the rule exists.
Deeply nested code isn't slow at runtime, but it's harder to follow, harder to test, and harder to change without breaking. A function that reaches four levels of indentation usually benefits from extracting a helper or combining conditions.
passPython doesn't allow an empty block. The parser sees a header with a colon and expects something indented under it. For a placeholder, use pass, which is a statement that does nothing.
These three uses all parse, run, and do nothing. The pass statement satisfies the requirement that a block contain at least one statement. Drop it once you replace it with real code.
Without pass, the same definitions raise IndentationError:
pass is most often a temporary placeholder while sketching the structure of a module or class. It's also useful in except clauses where the intent is to ignore an error.
Indentation rules are about which lines belong to which block. A single statement that's too long for one line is a separate question, and it doesn't change the indentation rules.
A long expression wrapped in parentheses, brackets, or braces can spread across several physical lines. The continuation lines don't have to follow indentation rules, but the convention is to align them for readability.
The parser sees one assignment statement here, not four. The whitespace inside the parentheses is formatting, not block structure.
For lines without brackets, a backslash \ at the end of the line acts as a continuation:
This works, but parenthesized continuation is preferred. A stray space after the backslash breaks it without warning, and the syntax doesn't read as cleanly. Save the backslash for cases where parentheses don't fit, like long with statements in older Python versions.
Continuation lines aren't new blocks. They're the same statement, written across multiple lines. The block-structure indentation rules only fire when a new statement begins.
A short tour of the mistakes that produce the indentation errors above.
What's wrong with this code?
The body of the function isn't indented. Python expects the lines inside the function to be indented past the def line. The fix is to indent return by four spaces.
What's wrong with this code?
The third print jumps to a deeper indent level than the previous two, but there's no new block to start. Inside one block, every line shares the same column. Drop the third print back to align with the others.
What's wrong with this code?
The second print is at indent level 0, so it's outside the for loop. It runs once, after the loop finishes, using whatever value product happened to have on the last iteration. That's typically not the intended behavior. To print both lines for each product, indent the second print to match the first.
What's wrong with this code?
The first body line uses 4 spaces. The second uses a tab. Python raises TabError. Convert the file to spaces only.
10 quizzes