Python Syntax and Indentation Rules

If you've written even a few lines of Python, you've probably noticed something other languages don't do: whitespace actually matters. This isn't a stylistic quirk — it's core to python syntax, and understanding it early will save you from a whole category of confusing errors later.

What is Python Syntax?

Syntax is simply the set of rules that govern how code has to be written for the language to understand it — where semicolons go (or don't), how you define a function, how blocks of code are grouped together. Every language has its own syntax, and Python's is widely considered one of the friendliest for beginners.

Part of that reputation comes from how close Python reads to plain English. There are no semicolons ending every line, no curly braces wrapping every block. What Python uses instead — and what genuinely surprises a lot of newcomers — is python indentation. Whitespace isn't just for readability in Python; it's a required part of the syntax itself. That's the focus of this article.

Why Python Uses Indentation Instead of Braces

The contrast with brace-based languages

In languages like C, Java, or JavaScript, code blocks are defined with curly braces { }. Indentation in those languages is purely a style choice — you could write everything on one line with no indentation at all, and the code would still run (though nobody would thank you for it). Here's a JavaScript-style example, just to illustrate the contrast:

if (x > 0) {
console.log("positive");
}

That code runs fine even with terrible indentation, because the braces — not the whitespace — define the block.

Indentation is Python's syntax, not a suggestion

Python takes a different approach entirely. Indentation isn't cosmetic — it's how Python knows which lines belong together as a block. Remove or misalign the indentation, and the code doesn't just look messy, it fails to run.

if x > 0:
    print("positive")

That indented line isn't a style choice. It's what tells Python "this line belongs inside the if block."

The role of the colon

Notice the colon (:) at the end of the if line. That colon is what signals a new indented block is about to begin. You'll see this same pattern across all of Python's block-starting statements:

if x > 0:
    print("positive")

for i in range(5):
    print(i)

while x < 10:
    x += 1

def greet(name):
    print(f"Hello, {name}")

class Dog:
    pass

Every one of those keywords — if, for, while, def, class — ends its header line with a colon, followed by an indented block underneath. Forget the colon, and Python will raise a syntax error before your code even runs.

The Rules of Indentation

Now for the specifics of python indentation rules.

The standard: 4 spaces per level

PEP 8, Python's official style guide, recommends 4 spaces per indentation level. This is the overwhelming convention across the Python ecosystem — most code you'll read, and most code you should write, follows it.

Technically, Python only requires that indentation be consistent — even a single space would work, as long as it's applied uniformly. But don't do that. Stick to 4 spaces; it's what every style guide, linter, and experienced developer expects.

All statements in a block must match

Every line within the same block needs identical indentation — not just "close enough."

# This works — both lines indented exactly 4 spaces
if x > 0:
    print("positive")
    print("still inside the block")

# This breaks — inconsistent indentation
if x > 0:
    print("positive")
      print("this line has extra spaces — error")

That second example will raise an error, because Python can't tell whether that last line is meant to be part of the same block or something new.

Never mix tabs and spaces

This is one of the most common sources of confusing bugs for beginners. Tabs and spaces can look identical in some editors while being completely different characters underneath. Mix them in the same file, and Python will often raise a TabError, refusing to guess which one you meant. The fix is simple: pick spaces (per PEP 8) and configure your editor to never insert a literal tab character.

How nested blocks work

Each additional level of nesting adds another indentation level:

for i in range(3):
    if i > 0:
        print(f"{i} is greater than zero")
        if i == 2:
            print("and this one is exactly 2")

Each if and for adds 4 more spaces to whatever's nested inside it. As you'll see in the last section, it's worth keeping this nesting as shallow as you reasonably can.

Common Indentation Errors and How to Fix Them

Sooner or later, you'll hit one of these. Here's what they mean and how to fix them.

IndentationError: expected an indented block

This happens when Python sees a colon expecting an indented block afterward, but the next line isn't indented at all:

if x > 0:
print("positive")  # Error — this needs to be indented

Fix: indent the line(s) that belong inside the block.

IndentationError: unexpected indent

The opposite problem — a line is indented when Python wasn't expecting a new block to start:

x = 5
    print(x)  # Error — no reason for this line to be indented

Fix: remove the unexpected indentation so the line aligns with the code around it.

Inconsistent spacing within the same block

Covered above, but worth restating: if some lines in a block use 4 spaces and others use 3 or 5, Python will throw an error rather than guess your intent.

Quick fixes

  • Configure your editor to "Insert Spaces." Most editors, including VS Code and PyCharm, let you set tab key presses to insert spaces instead of a literal tab character — do this once, and the whole category of tab/space mixing disappears.

  • Use an auto-formatter. Tools like Black or Ruff automatically reformat your code to consistent indentation (and other style rules) every time you save. If you set up VS Code or PyCharm following the previous article in this series, you likely already have Ruff available — turning on format-on-save eliminates most indentation errors before they happen.

Other Core Syntax Basics & Best Practices

A few more syntax fundamentals round out the picture.

Comments and docstrings

A single-line comment starts with #, and Python ignores everything after it on that line:

# This explains what the next line does
x = 5

A docstring, written with triple quotes, documents an entire function, class, or module, and typically sits as the first line inside it:

def greet(name):
    """Return a friendly greeting for the given name."""
    return f"Hello, {name}!"
Case sensitivity and naming conventions

Python is case-sensitive — name and Name are treated as two completely different variables. Beyond that technical rule, the community follows some strong naming conventions:

  • snake_case — for variables and function names: user_name, calculate_total()

  • PascalCase — for class names: class UserAccount:

  • UPPER_CASE — for constants: MAX_RETRIES = 5

These aren't enforced by the interpreter, but following them makes your code instantly familiar to any other Python developer who reads it.

Line continuation inside brackets

Long lines are easier to read when broken up, and Python allows this automatically inside parentheses, brackets, or braces — no special continuation character needed:

total = (
    first_value
    + second_value
    + third_value
)

If you're not inside a bracketed structure, you can use a trailing backslash \ to continue a line, though it's used far less often since it's more fragile and harder to read.

Keep nesting shallow

Deeply nested code — an if inside a for inside another if inside a while — gets hard to follow fast, both for you and anyone else reading it. If you notice your indentation creeping past three or four levels deep, it's usually a sign the logic can be simplified, often by breaking a chunk of it out into its own function. This isn't a strict Python syntax for beginners rule, but it's a habit worth building early — shallow, flat code is almost always easier to read and debug than deeply nested code.