Syntax and Indentation
How Python uses whitespace, PEP 8 style, and the tabs vs spaces convention
The previous page noted that Python uses indentation to delimit blocks of code. This page goes into detail: what counts as a block, what PEP 8 has to say about style, how to keep your editor honest, and why this choice sparked one of programming's most famous debates.
Why Python chose significant whitespace
Most languages in the 1980s and 1990s used braces ({ ... }) or
keywords (begin ... end) to mark blocks. Python took a different
path: Guido van Rossum observed that programmers already indent code
to show structure to humans, so why not make the compiler trust that
indentation? The result is that:
- Badly indented code will not run. A misaligned line is a syntax error, not a silent bug.
- Every Python codebase looks consistent. There is no "my braces on the same line" vs "my braces on the next line" religious war.
- Beginner-friendly. New programmers do not have to learn a special punctuation syntax; they just indent naturally.
This design choice is polarizing. Some find it liberating; others miss the explicit delimiters of C-like languages. Either way, it is non-negotiable in Python.
Blocks are indentation
In C, JavaScript, or Java, code inside a function or if is wrapped
in { ... }. In Python it is wrapped in a deeper indentation level:
The colon (:) at the end of def, if, for, while, try,
class, and so on signals "the next indented block belongs to me".
Think of the colon as Python's way of saying "here comes a block". It
replaces the opening brace { in C-like languages. The dedent
(returning to the previous indentation level) implicitly closes the
block.
What happens when indentation is wrong
If indentation is missing where Python expects it, you get an
IndentationError. If it is inconsistent, you get a TabError or a
silently broken program (in older Python 2 codebases).
Mixing tabs and spaces
Python 3 will raise a TabError if a single block mixes tabs and spaces. Python 2 allowed it and silently treated a tab as "however many spaces it takes to reach the next multiple of 8", which caused famously confusing bugs. Configure your editor to insert spaces when you press the Tab key, and you will never see this error.
The four-space rule (PEP 8)
PEP 8, Python's official style guide, says:
- Use four spaces per indentation level.
- Do not mix tabs and spaces inside the same block. Python 3
raises a
TabErrorif you try. - Limit lines to 79 characters (or 99 in modern codebases that
follow
black's default of 88). - Two blank lines between top-level functions and classes; one blank line between methods inside a class.
- One statement per line.
if x: do(); other()is legal but frowned upon.
Tools that enforce this for you:
blackreformats your code in one consistent style with almost no configuration. It is the most popular Python formatter.ruffis a very fast linter that rolls up most of the older tools (flake8,isort,pyupgrade).- Most editors (VS Code, PyCharm, Vim, Emacs) can run these on save.
The tabs vs spaces debate
We touched on this on the Python History page. The debate is older than Python itself and predates programming languages: it started with typewriters and word processors. The arguments in favor of each:
| Argument | Tabs | Spaces |
|---|---|---|
| Each developer picks their own width | ✅ Yes | ❌ No |
| Renders identically everywhere | ❌ No | ✅ Yes |
| Easier to align continuation lines | ❌ No | ✅ Yes |
Default in PEP 8, black, ruff | ❌ No | ✅ Yes |
| Allowed in Python 3 source | ✅ Yes (alone) | ✅ Yes (alone) |
| Accessibility: screen readers / low vision | ✅ Better | ❌ Worse |
The strongest argument for tabs
The accessibility argument is real: a developer with low vision can configure their editor to display a tab as 8 spaces for easier reading, while a sighted colleague can set it to 2 spaces to fit more code on screen. With spaces, everyone is locked into the same visual width.
The strongest argument for spaces
Tabs break alignment of continuation lines. Consider:
result = some_function(arg1, arg2,
arg3, arg4)If the file uses tabs, the alignment of arg3 depends on the tab width
of your editor. On one machine it looks perfect; on another it is
misaligned. Spaces render identically everywhere.
The community consensus
The community has effectively settled on four spaces. PEP 8 states:
Spaces are the preferred indentation method. Tabs should be used solely to remain consistent with code that is already indented with tabs.
For brand-new code, use four spaces. If you join a project that already uses tabs, stay consistent with what is there. Either way, configure your editor to insert one or the other automatically so you never produce mixed indentation.
The Silicon Valley incident
In the TV show Silicon Valley, an engineer quits over a tabs-vs-spaces argument. The scene is a joke, but it is rooted in reality: this debate has genuinely derailed code reviews and project contributions. Do not let it derail yours. Pick four spaces and move on.
Continuation lines
Python statements normally end at the end of a line. There are two ways to span multiple lines:
- Implicit continuation inside
(),[], or{}. This is the preferred style. - Explicit continuation with a backslash
\at end of line.
PEP 8 recommends avoiding backslash continuation where possible. Modern
code wraps long lines in parentheses even when they are not strictly
needed (e.g. for a long if condition).
Semicolons (please do not)
Python technically allows ; to separate statements on one line. The
community consensus is do not use it. PEP 8 says "avoid it" in
polite language; the real sentiment is "never do this".
The only exception is in one-liners passed to python -c:
python -c "import sys; print(sys.version)"Even there, most people avoid ; and use newlines instead.
Real-world comparison: Python vs other languages
Here is how other popular languages handle indentation:
| Language | Block delimiter | Indentation | Community standard |
|---|---|---|---|
| Python | Indentation | Required | 4 spaces (PEP 8) |
| JavaScript | { ... } | Optional | 2 spaces (Prettier default) |
| Java | { ... } | Optional | 4 spaces |
| C / C++ | { ... } | Optional | Varies (Google: 2, Linux kernel: tabs) |
| Go | { ... } | Optional | Tabs (gofmt enforces this) |
| Ruby | do ... end or { ... } | Optional | 2 spaces |
| Rust | { ... } | Optional | 4 spaces (rustfmt default) |
| Haskell | Indentation or { ; } | Optional but idiomatic | 2 spaces |
Python is unique in making indentation mandatory. Haskell has optional significant whitespace, but most Haskellers use braces to avoid the complexity. Python goes all-in.
Challenges
The function average below has indentation problems and a stray semicolon. Rewrite the body so that:
- It uses four-space indentation throughout.
- It has no semicolons.
- It returns the arithmetic mean of
numbers, or0if the list is empty.
The hidden tests will call average with several inputs.
Rewrite the function long_sum below to use implicit continuation inside parentheses instead of backslash continuation. The function should return the sum of all its arguments.
Do not change the function's behavior, just improve its style.
Test your knowledge
What delimiter does Python use to mark code blocks?
Curly braces { ... }
Keywords begin ... end
Indentation
Parentheses ( ... )
What does PEP 8 recommend for indentation?
A single tab character
Two spaces
Four spaces
Eight spaces
What error does Python 3 raise if you mix tabs and spaces in the same block?
SyntaxError
IndentationError
TabError
RuntimeError
Which tool is the most popular Python code formatter?
pylint
flake8
black
mypy
Now that the rules of the road are clear, we will start using them to build real programs, starting with variables and data types.