Syntax, indentation, and comments
The rules Python enforces: indentation blocks, statements, line continuation, comments, and docstrings.
Why this matters in AI / ML / GenAI
Indentation errors are the first wall every beginner hits, and they still bite experienced engineers who paste code out of a notebook or a chatbot. Knowing the rules means you can fix the error instead of guessing.
Indentation is the block structure
Most languages mark blocks with braces. Python uses indentation, so whitespace is part of the grammar.
Rules that matter:
- A colon
:ends the line that opens a block (if,for,while,def,class,try,with). - Everything in the block is indented by the same amount.
- Four spaces per level is the universal convention. Never mix tabs and spaces — Python 3 rejects it with
TabError. - The block ends when indentation returns to the outer level.
Two errors you will see:
IndentationError: expected an indented block— you opened with a colon but did not indent.IndentationError: unindent does not match any outer indentation level— the levels are inconsistent, usually mixed tabs and spaces from copy-paste.
Configure your editor to insert spaces when Tab is pressed. That one setting removes an entire class of errors.
Statements and line length
One statement per line, no semicolons needed. Python accepts a = 1; b = 2 but nobody writes that.
Long lines break naturally inside brackets (), [], {} — no continuation character required. This is why function calls with many arguments are written one argument per line.
The backslash \ also continues a line, but it is fragile (a trailing space after it breaks the file). Prefer brackets.
PEP 8 suggests 79 characters; most teams settle on 88 or 100 and let a formatter such as ruff or black enforce it.
Comments and docstrings
# starts a comment to end of line. Python has no block comment syntax.
A docstring is a string literal as the first statement of a module, function, or class. Unlike a comment it is stored on the object and readable at runtime through help() or __doc__. Editors show it on hover, and API documentation is generated from it.
Comment the why, not the what. # add 1 to i is noise. # offset by 1 because the API is 1-indexed is worth keeping.
Naming conventions carry meaning in Python:
| Style | Used for |
|---|---|
snake_case | variables, functions, modules |
PascalCase | classes |
UPPER_CASE | constants |
_leading | internal, do not touch from outside |
__dunder__ | Python's own special methods |
Copy-paste examples
Copy into your own editor, or load one into the compiler below and press Run.
Indentation defines the block
The indented lines belong to the if; the last line always runs.
score = 0.85
if score > 0.7:
print("high confidence")
print("still inside the if block")
print("outside the block — always runs")
for i in range(3):
if i % 2 == 0:
print(i, "even")
else:
print(i, "odd")Breaking long lines inside brackets
No backslash needed. This is how real config and API calls are written.
config = {
"model": "gpt-4.1-mini",
"temperature": 0.2,
"max_tokens": 512,
}
def train(
model_name,
epochs=3,
learning_rate=2e-5,
batch_size=16,
):
return f"{model_name}: {epochs} epochs at lr={learning_rate}"
print(config)
print(train("bert-base", epochs=5))Docstrings vs comments
The docstring is retrievable at runtime; the comment is not.
def cosine_similarity(a, b):
"""Return the cosine similarity between two equal-length vectors.
Args:
a: First vector as a list of floats.
b: Second vector as a list of floats.
Returns:
A float between -1.0 and 1.0.
"""
# guard against a zero vector, which would divide by zero
dot = sum(x * y for x, y in zip(a, b))
norm_a = sum(x * x for x in a) ** 0.5
norm_b = sum(y * y for y in b) ** 0.5
if norm_a == 0 or norm_b == 0:
return 0.0
return dot / (norm_a * norm_b)
print(round(cosine_similarity([1, 2, 3], [2, 4, 6]), 4))
print(cosine_similarity.__doc__.splitlines()[0])Naming conventions in one file
Style signals intent to the next reader — follow it exactly.
MAX_TOKENS = 4096 # constant
class PromptBuilder: # class: PascalCase
def __init__(self, system_prompt):
self.system_prompt = system_prompt # public attribute
self._call_count = 0 # internal, leave alone
def build(self, question): # method: snake_case
self._call_count += 1
return f"{self.system_prompt}\n\nQ: {question}"
builder = PromptBuilder("Be concise.")
print(builder.build("What is Python?"))
print("MAX_TOKENS:", MAX_TOKENS)Fix the indentation
Try it — in-browser Python
The else block is under-indented. Line it up with the if, then press Run.
Output
Python runs in your browser. First run downloads the runtime.
Press Run (or Ctrl+Enter) to execute.
CPython in WebAssembly. Stdlib works. NumPy, pandas, scikit-learn and Matplotlib load on demand, and charts render below. No input(), no GPU, no network installs.
Takeaways
- Indentation is syntax: four spaces per level, never mix tabs and spaces.
- Break long lines inside brackets rather than with a backslash.
- Docstrings are readable at runtime; comments explain why, not what.