Follow the Python PEP 8 Style Guide

Learn the essential rules of PEP 8, Python's official style guide, and how to apply consistent formatting that every Python developer recognizes.

7 min read

PEP 8 is the official style guide for Python code. PEP stands for Python Enhancement Proposal, and PEP 8 was written to give the Python community a shared set of formatting conventions. When every project follows the same rules for indentation, line length, whitespace, and naming, Python developers can move between codebases without needing to adjust to a new visual style each time. The guide was originally authored by Guido van Rossum, Python's creator, along with Barry Warsaw, and has been updated over the years as the language evolved.

Following PEP 8 is not about being pedantic. It is about reducing the mental effort of reading code. When every file uses the same indentation and whitespace patterns, your brain stops noticing the formatting and starts focusing on the logic. PEP 8 extends that consistency to every visual aspect of your code.

Indentation: 4 spaces per level

The most fundamental PEP 8 rule: use 4 spaces for each indentation level. This applies to function bodies, loop bodies, if blocks, class definitions, and every other block in Python.

pythonpython
def calculate_total(items, tax_rate):
    subtotal = sum(items)
    if subtotal > 0:
        tax = subtotal * tax_rate
        return subtotal + tax
    return 0

Do not use tabs. Configure your editor to insert spaces when you press the Tab key. Most Python-aware editors do this by default.

For line continuations inside parentheses, brackets, or braces, PEP 8 offers two styles. The hanging indent with a trailing comma is the more common choice in modern Python codebases because it produces cleaner diffs when arguments are added or removed:

pythonpython
# Hanging indent with trailing comma (preferred by many teams)
result = some_function(
    first_argument,
    second_argument,
    third_argument,
)

Line length: 79 characters for code, 72 for docstrings

PEP 8 recommends keeping lines at or under 79 characters. This limit persists because shorter lines are easier to read and compare side by side in diffs. For docstrings and comments, the limit is 72 characters because docstrings are often indented inside a function or class body.

pythonpython
def process_records(filepath, encoding="utf-8"):
    """Read records from a file and return them as a list of dictionaries.
 
    Each line in the file is expected to be a JSON object. Empty lines
    and lines starting with # are skipped.
    """
    records = []
    with open(filepath, encoding=encoding) as fh:
        for line in fh:
            stripped = line.strip()
            if not stripped or stripped.startswith("#"):
                continue
            records.append(json.loads(stripped))
    return records

When a line is too long, put the expression inside parentheses and let implicit line continuation handle the break. Use a backslash only as a last resort for with statements and assert statements.

Blank lines: separate top-level definitions

Use two blank lines before top-level function and class definitions. Use one blank line before method definitions inside a class. Use blank lines sparingly inside functions to separate logical sections.

pythonpython
import os
import sys
 
 
def load_config():
    """Read configuration from the default config file."""
    path = os.path.expanduser("~/.myapp/config.toml")
    return _parse_config(path)
 
 
def _parse_config(path):
    """Internal helper to parse a TOML config file."""
    with open(path) as fh:
        return toml.load(fh)

Inside a class, use a single blank line between methods. The two blank lines between top-level definitions create clear visual separation:

pythonpython
class ConfigManager:
    """Manages application configuration with caching."""
 
    def __init__(self):
        self._cache = {}
 
    def get(self, key, default=None):
        if key not in self._cache:
            self._cache[key] = self._load_key(key)
        return self._cache.get(key, default)
 
    def _load_key(self, key):
        """Internal: load a single config key from disk."""
        return self._reader.read(key)

Imports: one per line, grouped and sorted

Each import should be on its own line. Organize imports in three groups separated by a blank line: standard library imports, third-party imports, and local application imports. Alphabetize within each group.

pythonpython
# Standard library
import os
import sys
from datetime import datetime, timezone
from pathlib import Path
 
# Third-party
import requests
from pydantic import BaseModel
 
# Local
from myapp.config import settings
from myapp.utils import format_date

Avoid wildcard imports (from module import *). They pollute the namespace and make it impossible to trace where a name came from.

Whitespace around operators and after commas

Place a single space on each side of assignment operators, comparison operators, and arithmetic operators. Do not add spaces around = in keyword arguments or default parameter values. Do not add a space immediately inside parentheses, brackets, or braces.

pythonpython
# Correct spacing around operators
x = 5
total = price * quantity
result = (a + b) * (c - d)
items = [1, 2, 3]
 
# No spaces around = in keyword arguments
response = requests.get(url, timeout=10)
 
# No spaces inside brackets
values = [1, 2, 3]
point = (x, y)
mapping = {"key": "value"}

For slices, treat the colon as a binary operator with equal spaces on each side when both start and end are present. When one side is omitted, omit the space on that side:

pythonpython
items[1:4]      # space on both sides of colon
items[:4]       # no space before colon
items[1:]       # no space after colon
items[:]        # no spaces
items[1:4:2]    # step also gets spaces

Naming conventions

PEP 8 defines a clear naming scheme for every kind of Python identifier. Consistent naming makes it obvious at a glance whether a name refers to a function, a class, or a constant.

KindConventionExample
Functionssnake_casecalculate_total, fetch_user
Variablessnake_caseuser_count, max_retries
ClassesPascalCaseUserAccount, HttpClient
ConstantsUPPER_CASEMAX_CONNECTIONS, DEFAULT_PORT
Methodssnake_caseget_balance, set_name
Modulessnake_casedata_processor.py, email_utils.py
Packagesshort lowercasemypackage, utils
Private internals_leading_underscore_cache, _parse_config

Private names start with a single leading underscore. This is a convention, not a language-level access restriction. It tells other developers that the name is an implementation detail. For the reasoning behind each convention in this table, see Name Python Variables, Functions, and Classes.

Avoid single-letter names except in short loops and comprehensions where the meaning is obvious from context. Never use lowercase l, uppercase O, or uppercase I as single-character names because they are easily confused with the digits 1 and 0.

Comments: explain why, not what

Write comments in complete sentences with a space after the #. The comment should explain the reasoning behind non-obvious code, not restate what the code already says.

pythonpython
# Bad: restates the code
x = x + 1  # increment x
 
# Good: explains the decision
x = x + 1  # compensate for the 0-based index in the external API

Docstrings are triple-quoted strings that describe modules, classes, and functions. PEP 257 defines the docstring conventions that complement PEP 8.

Automate formatting with Ruff and Black

You do not need to check every PEP 8 rule manually while coding. Python has mature formatting tools that apply the rules automatically.

Ruff is a fast Python linter and formatter written in Rust. It enforces hundreds of rules including all of PEP 8 and can auto-fix most violations:

texttext
pip install ruff
ruff check .          # check for issues
ruff check --fix .    # auto-fix what can be fixed
ruff format .         # format code consistently

Black is a popular opinionated formatter that takes PEP 8 and applies it strictly with a few intentional deviations, such as preferring double quotes and a default line length of 88 characters.

Both tools integrate into your editor to format on save. A typical modern Python project uses Ruff for both linting and formatting. PEP 8 covers layout and formatting, but idiomatic Python goes further; see Write Pythonic Code in Python for the patterns that come next. Configure Ruff in pyproject.toml:

tomltoml
[tool.ruff]
line-length = 88
 
[tool.ruff.format]
quote-style = "double"

This setup catches violations as you type and fixes them on save. You spend zero mental energy on whitespace and can focus entirely on the logic.

Rune AI

Rune AI

Key Insights

  • PEP 8 is the official Python style guide covering indentation, line length, whitespace, imports, and naming.
  • Use 4 spaces per indentation level and keep lines at or under 79 characters where practical.
  • Organize imports in three groups: standard library, third-party, and local.
  • Use snake_case for functions and variables, PascalCase for classes, and UPPER_CASE for constants.
  • Automate formatting with Ruff or Black so you do not need to think about spacing during development.
RunePowered by Rune AI

Frequently Asked Questions

What is PEP 8?

PEP 8 is the official style guide for Python code. It defines conventions for indentation, line length, blank lines, imports, naming, comments, and whitespace. Following PEP 8 makes your code consistent with the wider Python ecosystem.

Do I need to memorize every PEP 8 rule?

No. The most important rules can be automated with formatters like Ruff or Black, and linters will catch the rest. Focus on understanding the reasoning behind key rules, and let tools enforce the details.

Conclusion

PEP 8 is not about making every Python file look identical. It is about removing the cognitive friction of inconsistent formatting so readers can focus on what the code does. Learn the rules, automate enforcement, and spend your mental energy on logic instead of layout.