GICT knowledge base
Python Indentation: Blocks, Four-Space Style and Error Fixes
Published
Indentation is part of Python’s syntax: it groups statements into blocks. It also determines behavior, so code can be valid Python and still do the wrong thing because a statement belongs to the wrong block. Use four spaces per indentation level as recommended by PEP 8.
The examples below were executed with Python 3.12.14. Intentionally invalid source is stored in strings and checked with compile(), rather than presented as working code to paste into a program.
Align statements in the same block
After a multi-line if, loop, function or class header, indent its body. Nested blocks add another level. A dedent returns to an earlier enclosing level. In this example, return belongs to the function, not the loop, so every item is considered.
def positive_values(values):
result = []
for value in values:
if value > 0:
result.append(value)
return result
assert positive_values([-2, 3, 0, 5]) == [3, 5]
assert positive_values([]) == []
print(positive_values([-2, 3, 0, 5]))
Separate style from syntax
Four spaces is the standard style, not a rule that every valid block must contain exactly four leading spaces. A consistently indented two-space block can parse. Prefer four spaces so teammates and tools see a familiar structure, and follow an established repository’s style when editing it.
two_space_source = "if True:\n result = 7\n"
namespace = {}
exec(compile(two_space_source, "two-space-example", "exec"), namespace)
assert namespace["result"] == 7
class Settings:
def __init__(self, enabled):
self.enabled = enabled
assert Settings(True).enabled is True
print(namespace["result"])
The fixed string above is a syntax demonstration, not a reason to execute untrusted strings. Never pass user-provided source to exec() as an input-processing technique.
Catch missing, inconsistent and ambiguous indentation
An empty body needs a real statement such as pass; a comment alone does not fill it. A dedent to a level that was never opened raises IndentationError. Mixing tabs and spaces can raise TabError when the meaning depends on how tab width is interpreted. Use spaces consistently rather than relying on how an editor renders tabs.
invalid_examples = [
("if True:\n# only a comment\n", IndentationError),
("if True:\n x = 1\n y = 2\n", IndentationError),
("if True:\n\tx = 1\n y = 2\n", TabError),
]
for source, expected_error in invalid_examples:
try:
compile(source, "invalid-indentation-example", "exec")
except expected_error:
pass
else:
raise AssertionError("Invalid indentation was accepted")
if True:
pass
print("All three invalid examples were rejected")
Check meaning even when the parser accepts the code
Moving an update outside a loop may keep the program syntactically valid while changing the result. These two functions deliberately differ: one records every prefix total, while the other records only the final total. A formatter cannot infer which behavior you wanted.
def prefix_totals(values):
total = 0
result = []
for value in values:
total += value
result.append(total)
return result
def final_total(values):
total = 0
result = []
for value in values:
total += value
result.append(total)
return result
assert prefix_totals([2, 3, 4]) == [2, 5, 9]
assert final_total([2, 3, 4]) == [9]
assert prefix_totals([]) == []
assert final_total([]) == [0]
print(prefix_totals([2, 3, 4]), final_total([2, 3, 4]))
Distinguish continuation indentation from a new block
Inside parentheses, brackets or braces, a statement can continue over several physical lines. Align those lines for readability; they are not separate suites. A colon in a dictionary, slice or lambda is not a request to open an indented statement block. Parentheses generally make multi-line expressions easier to maintain than explicit backslash continuations.
limits = {
"soft": 10,
"hard": 20,
}
result = (
limits["soft"]
+ limits["hard"]
)
assert result == 30
assert [1, 2, 3, 4][1:3] == [2, 3]
print(result)
Fix pasted code without damaging its data
- Enable visible whitespace in your editor and inspect the entire surrounding block, not only the reported line.
- Convert indentation to spaces with the editor’s indentation command. Do not globally replace whitespace inside strings, templates or sample data.
- Check for non-breaking spaces copied from formatted pages. They are not ordinary Python indentation spaces.
- Use a Python-aware formatter after syntax errors are fixed, then run tests that check the intended behavior.
Verification: All five blocks ran independently. Tests cover valid two-space syntax, three expected indentation errors, nested behavior and empty input. References: Python’s indentation rules and PEP 8 indentation style. Related: loop control.
Related guides
How to Deploy a FastAPI App with Gunicorn and Nginx
Deploy FastAPI behind Nginx with Gunicorn and systemd. Separate application packages, check HTTPS and verify worker replacement and application health.
PythonLearn How to Modify Python Dictionary Values Easily
Learn how to modify Python dictionary values effectively with various methods, including direct assignment, update(), and comprehensions, along with common mistakes to...
PythonPython Set Copy Tutorial: Avoid Common Mistakes and Data Errors
Learn how to copy sets in Python safely and efficiently while avoiding common mistakes. Explore methods like copy(), set(), and deepcopy().
PythonDifferent Methods to Loop Through Sets in Python (Beginner-Friendly Guide)
Learn diverse methods to loop through sets in Python, including practical examples and use cases, ideal for beginners looking to master set operations.
