Python PEP 8 & style, explained
PEP 8 is the official style guide for Python code. It isn't about pedantry — consistent style makes code readable across a whole community, so anyone can drop into any project and feel at home. Here are the rules that matter most, plus the modern tooling that applies them for you.
Naming conventions
Names carry meaning, and PEP 8 assigns a casing to each kind of name. Following them lets readers tell a class from a constant at a glance.
module_name.py # lowercase_with_underscores
class HttpClient: # CapWords / PascalCase for classes
MAX_RETRIES = 3 # UPPER_CASE for constants
def send_request(self): # lowercase_with_underscores for functions/methods
local_var = 1 # same for locals
self._internal = 2 # single leading underscore = "non-public"
A single trailing underscore avoids clashing with keywords (class_); a double leading
underscore triggers name mangling (__x), so reserve it for that specific purpose.
Layout: indentation and line length
Use 4 spaces per indent level, never tabs. Keep lines to 79 characters (PEP 8's limit; many teams relax to 88 or 99). Break long lines inside parentheses, not with backslashes.
# Preferred — implicit continuation inside brackets
result = some_function(argument_one, argument_two,
argument_three, argument_four)
total = (first_value
+ second_value
+ third_value) # operators at the start of the line read better
Surround top-level functions and classes with two blank lines, and methods inside a class with one.
Imports
Put imports at the top of the file, one module per line, grouped: standard library, then third-party, then local — with a blank line between groups.
import os
import sys
import requests
from flask import Flask
from myapp.models import User
Avoid wildcard imports (from module import *) — they pollute the namespace and hide where
names come from.
Whitespace rules
PEP 8 is specific about spacing. Spaces around binary operators and after commas; no space inside brackets or before a call's parenthesis.
# Good
x = (a + b) * c
func(arg, other=1)
items[1:3]
# Bad
x=( a+b )*c
func (arg , other = 1)
items[1 : 3]
One exception worth remembering: in a keyword argument or default, no spaces around =
(other=1), but in a normal assignment you do use them (x = 1).
Idiomatic comparisons and checks
PEP 8 codifies several "Pythonic" habits: compare to None with is, test emptiness via
truthiness, and use isinstance over type(...) ==.
if value is None: ... # not == None
if not items: ... # not len(items) == 0
if isinstance(x, int): ... # not type(x) == int
These read more clearly and handle subclasses and edge cases correctly.
Let tools enforce it
You shouldn't hand-apply PEP 8 — automate it. black is an opinionated formatter that rewrites code to a consistent style; ruff is a fast linter (and formatter) that flags violations and many bugs. Run them in CI and a pre-commit hook.
ruff check . # lint
ruff format . # or: black . — auto-format
With formatting automated, style stops being a code-review topic entirely.
Recap
PEP 8 standardises Python style so code is readable everywhere: snake_case for
functions/variables/modules, PascalCase for classes, UPPER_CASE for constants;
4-space indents and ~79–88 character lines with breaks inside brackets; imports grouped
stdlib / third-party / local; consistent whitespace around operators but not inside
brackets; and idioms like is None, truthiness checks, and isinstance. Don't apply it by
hand — let black and ruff enforce it automatically.