PEP 8 & Style Reference
PEP 8 is Python's official style guide. This page distills the rules that matter most in modern Python 3.14 projects with ruff enforcement.
Recipe
[tool.ruff]
line-length = 88
target-version = "py314"
[tool.ruff.lint]
select = ["E", "W", "F", "I"]uv run ruff format . && uv run ruff check --fix .When to reach for this:
- Resolving style debates in code review
- Configuring ruff for a new project
- Onboarding developers from other languages
PEP 8 Reference
| Topic | Rule | Example |
|---|---|---|
| Indentation | 4 spaces, no tabs | def foo(): + 4-space body |
| Line length | 88 chars (Black/ruff default) | Wrap with parentheses |
| Imports | stdlib, third-party, local | Blank line between groups |
| Names | snake_case, CapWords classes | my_function, MyClass |
| Whitespace | space around operators | x = 1, a + b, not x=1 |
| Comments | complete sentences | # Calculate total, not tax. |
| Strings | consistent quote style | double quotes (ruff default) |
Working Example
# Good PEP 8 style
from __future__ import annotations
import os
from pathlib import Path
import httpx
from pydantic import BaseModel
class Invoice(BaseModel):
id: int
amount: float
customer_email: str
def fetch_invoice(client: httpx.Client, invoice_id: int) -> Invoice:
response = client.get(f"/invoices/{invoice_id}")
response.raise_for_status()
return Invoice.model_validate(response.json())
def main() -> None:
base = os.environ.get("API_URL", "http://localhost:8000")
with httpx.Client(base_url=base) as client:
invoice = fetch_invoice(client, 42)
print(invoice.customer_email)
if __name__ == "__main__":
main()What this demonstrates:
- Two blank lines before top-level functions and classes
- Imports grouped and sorted
- Type hints on public functions
if __name__guard for script execution
Deep Dive
Naming Conventions
| Type | Convention | Example |
|---|---|---|
| Module | lowercase | my_module.py |
| Class | CapWords | InvoiceService |
| Function | snake_case | calculate_total |
| Constant | UPPER_SNAKE | MAX_RETRIES |
| Private | leading _ | _internal_helper |
| Magic methods | dunder | __init__, __str__ |
Line Breaking
# Preferred: hanging indent with parentheses
result = some_function(
long_argument_one,
long_argument_two,
long_argument_three,
)
# Dict/list trailing comma ok
settings = {
"host": "localhost",
"port": 8000,
}Gotchas
- Tabs vs spaces - syntax error on mix. Fix: editor inserts spaces; ruff enforces.
- Trailing whitespace - noisy diffs. Fix: pre-commit
trailing-whitespacehook. - Wildcard imports -
from module import *pollutes namespace. Fix: explicit imports. - Mutable default arguments -
def f(items=[]):shared state. Fix:Nonedefault, create inside. - Comparing to None - use
is None, not== None.
Alternatives
| Alternative | Use When | Don't Use When |
|---|---|---|
| Google Python style | Google codebase interop | Standard PEP 8 project |
| numpy docstring style | Scientific computing | General web apps |
| Manual formatting | Never | Always use ruff format |
FAQs
79 or 88 char lines?
88 is the modern default (Black/ruff). Consistency matters more than the exact number.
Single or double quotes?
Pick one via formatter. ruff format defaults to double quotes.
When to ignore PEP 8?
Generated code, migrations, and rare cases where breaking hurts readability. Document # noqa.
Does ruff replace PEP 8 review?
Yes for mechanical rules. Human review still needed for naming quality and structure.
What is E501?
Line too long. Usually ignored when using ruff format.
Type hints and PEP 8?
PEP 484 type hints complement PEP 8. See PEP 484 for typing style.
Blank lines in methods?
One blank line between methods in a class. Two blank lines between top-level definitions.
Shebang line?
#!/usr/bin/env python3 for executable scripts only.
Encoding declaration?
Not needed in Python 3. UTF-8 is default.
Where is the full PEP 8?
peps.python.org/pep-0008/ - this page is the practical distillation.
Related
- 50 Python Rules - broader rules
- Ruff Setup - enforcement tooling
- Black / Ruff Format - formatting
- Linting Best Practices - team policy
Stack versions: This page was written for Python 3.14.0, FastAPI 0.115+, Django 5.2, Flask 3.1, Pydantic 2, PyTorch 2.6+, pandas 2.2+, Polars 1.x, ruff 0.9+, and uv 0.6+.