Python Syntax and PEP 8

Python uses indentation to define code blocks. Learn the rules, naming conventions, and the PEP 8 style guide that makes Python code consistent and readable.

Beginner 9 min read 8 examples

Indentation as Structure

Python uses indentation (whitespace at the start of a line) to define code blocks. This is not optional formatting - it is the syntax. The standard is 4 spaces per level.

Python
# The colon : starts a new block; the indented lines ARE the block
if True:
    print("indented - inside the if block")
    print("also inside the if block")
print("back to top level - outside the if")

# Functions use the same pattern
def greet(name):
    message = f"Hello, {name}!"   # 4 spaces in
    return message                 # 4 spaces in

# Nested blocks - each level adds 4 more spaces
for i in range(3):
    if i % 2 == 0:
        print(f"{i} is even")   # 8 spaces in
    else:
        print(f"{i} is odd")    # 8 spaces in

# Indentation error examples
# if True:
# print("wrong")  # IndentationError: expected an indented block
#
# if True:
#     print("ok")
#       print("too much indent")  # IndentationError: unexpected indent
Never mix tabs and spaces

Python 3 raises a TabError if you mix tabs and spaces for indentation. Configure your editor to insert 4 spaces when you press Tab for Python files. In VS Code: bottom-right status bar shows "Spaces: 4" when correctly configured for Python.

Statements and Lines

Python
# One statement per line - no semicolons needed
x = 10
y = 20
z = x + y

# Multiple statements per line using ; (avoid in practice)
a = 1; b = 2; c = 3  # works but PEP 8 discourages it

# Line continuation with backslash \
total = 1 + 2 + 3 + \
        4 + 5 + 6     # treated as one long line

# Implicit continuation inside brackets (preferred)
result = (1 + 2 + 3 +
          4 + 5 + 6)   # parentheses allow continuation without \

# Also works inside [] and {}
fruits = [
    "apple",
    "banana",
    "cherry",
]  # trailing comma is fine and encouraged

config = {
    "host": "localhost",
    "port": 5432,
    "database": "mydb",
}

# Function calls with many arguments
result = some_function(
    argument_one,
    argument_two,
    keyword=value,
)

# pass - placeholder for empty blocks (functions, classes, loops)
def todo():
    pass  # not implemented yet

class EmptyClass:
    pass

Naming Conventions

Python has well-established naming conventions defined in PEP 8. Following them makes your code recognizable to any Python developer.

TypeConventionExample
Variablessnake_caseuser_name, total_price
Functionssnake_caseget_user(), calculate_total()
ConstantsUPPER_SNAKE_CASEMAX_SIZE, API_KEY
ClassesPascalCaseUserProfile, HttpClient
Moduleslowercase (short)utils, config
Packageslowercase (short, no underscore)mypackage
Private (by convention)_leading_underscore_internal_method
Name mangling__double_leading__private_attr
Dunder methods__double_both____init__, __str__
Python
# Variables and functions: snake_case
user_name = "Alice"
total_score = 95

def calculate_average(scores):
    return sum(scores) / len(scores)

# Constants: UPPER_SNAKE_CASE
MAX_RETRIES   = 3
DEFAULT_TIMEOUT = 30

# Classes: PascalCase
class UserAccount:
    def __init__(self, username):
        self.username = username        # public attribute
        self._cache   = {}              # private by convention
        self.__secret = "hidden"        # name-mangled (harder to access from outside)

    def get_username(self):             # public method
        return self.username

    def _validate(self):                # private by convention
        pass

# _ alone is used for throwaway variables
for _ in range(5):
    print("hello")  # _ means "I don't need this loop variable"

x, _, z = (1, 2, 3)  # ignore the middle value

PEP 8 Style Guide

PEP 8 is the official Python style guide. Most Python projects enforce it. The key rules:

Python
# Imports: one per line, at the top, grouped
import os          # standard library
import sys

import requests    # third-party (blank line separates groups)

from myapp import utils  # local imports (blank line separates)

# NOT recommended:
# import os, sys  (multiple on one line)
# import sys, os  (wrong order)

# Blank lines:
# 2 blank lines before/after top-level functions and classes
# 1 blank line between methods inside a class

class MyClass:

    def method_one(self):
        pass

    def method_two(self):  # 1 blank line between methods
        pass


def top_level():  # 2 blank lines before top-level definition
    pass


# Spaces around operators
x = 10         # NOT x=10
y = x + 1      # NOT x+1 or x +1
z = x * 2 + y  # spaces around all operators

# No spaces inside brackets
print(x)       # NOT print( x )
fruits[0]      # NOT fruits[ 0 ]
d = {"k": "v"} # NOT d = { "k" : "v" }

# Spaces after commas, colons in dicts
result = func(1, 2, 3)        # NOT func(1,2,3)
d = {"key": "value"}          # NOT d = {"key" :"value"}

# Max line length: 79 chars (88 with Black)
# Automate: pip install black; black myfile.py

Whitespace Rules

Python
# Comparison operators - spaces on both sides
if x == 10:
    pass
if x is None:
    pass
if x is not None:
    pass

# Default argument values - NO spaces around = in function signatures
def connect(host, port=5432, timeout=30):  # correct
    pass

def connect(host, port = 5432):  # wrong (PEP 8 violation)
    pass

# But spaces in keyword arguments when calling:
connect(host="localhost", port=5432)  # OK
# Or without spaces:
connect(host="localhost", port=5432)  # both are acceptable for keyword args

# Avoid extra whitespace
spam(ham[1], {eggs: 2})     # correct
spam( ham[ 1], { eggs: 2})  # wrong

# Trailing whitespace - delete it (editors can do this automatically)
# Blank lines at end of file - exactly 1

Type Hints (Introduction)

Python 3.5+ supports optional type hints. They do not affect runtime behavior but help IDEs, type checkers (mypy), and other developers understand your code.

Python
# Variable annotation
name: str = "Alice"
age: int  = 30
price: float = 19.99
active: bool = True

# Function with type hints
def greet(name: str) -> str:
    return f"Hello, {name}!"

def add(a: int, b: int) -> int:
    return a + b

# Type hints don't enforce types at runtime
def process(x: int) -> None:
    print(x)

process("not an int")  # runs fine - no error at runtime
# But mypy or Pylance will flag this as a type error

# Type hints are documented here; full coverage in the Type Hints lesson
# from typing import Optional, List, Dict
# def find(items: List[str], key: str) -> Optional[str]: ...
Use Black to autoformat your code

Black is the most widely used Python auto-formatter. It applies PEP 8 and its own opinionated rules consistently: pip install black, then black yourfile.py. Configure VS Code to run Black on save. You will never argue about formatting again.

Frequently Asked Questions

PEP 8 specifies 4 spaces per indentation level. Never mix tabs and spaces - Python 3 raises an error if you do. Most editors auto-convert tab keypresses to 4 spaces when working on Python files. The key rule is consistency: pick one (4 spaces is the standard) and never mix.

Yes, using a semicolon: x = 1; y = 2; z = 3. But PEP 8 discourages this for code clarity - use separate lines instead. The only common exception is a very simple if with a trivial body: if x: return x. In practice, keep one statement per line.

PEP 8 is Python's official style guide (Python Enhancement Proposal 8). It covers naming, whitespace, line length, imports, and more. Following it makes your code readable to other Python developers. It is not enforced by the interpreter - code runs fine without it - but all major Python projects and professional codebases follow PEP 8. Use a linter like flake8 or ruff, and a formatter like black, to automate compliance.

PEP 8 recommends a maximum of 79 characters per line for code, and 72 for docstrings/comments. In practice, many projects allow 88-120 characters (Black formatter defaults to 88). Long lines can be broken using backslash \ continuation or implicit continuation inside brackets. For readability, break lines rather than writing one very long line.