PEP 8 is the official style guide for Python code. It describes how to name things, lay out code, use whitespace and order imports so that Python written by different people looks familiar. Consistent style makes code faster to read and review, and makes real problems stand out instead of being lost among formatting differences.
# Before
def RemainingPlaces(Cap,enr):return Cap-enr
# After
def remaining_places(capacity, enrolled):
return capacity - enrolledThis lesson covers the conventions you'll use daily, explains why they exist, and shows how formatters and linters apply them automatically, so you rarely have to think about them. The examples tidy a swimming club's enrolment script.
Readable naming
| What | Convention | Example |
|---|---|---|
| Variables, functions, methods, modules | snake_case | remaining_places, enrolment.py |
| Classes, exceptions | CapWords | SwimClass, EnrolmentError |
| Constants | UPPER_SNAKE_CASE | MAX_SWIMMERS |
| Internal names | leading underscore | _validate_age |
| Clash with a keyword | trailing underscore | class_, type_ |
Conventions only help if the names themselves carry meaning. remaining_places = capacity - enrolled explains itself; r = c - e follows the style rules and still tells the reader nothing. Avoid single-letter names except for short loops and well-known maths (i, x, y), and avoid l, O and I, which look like 1 and 0 in many fonts. Functions are usually named for what they do (calculate_fee) and booleans for the question they answer (is_full, has_paid).
Whitespace
PEP 8's main layout rules:
- 4 spaces per indentation level, never tabs mixed with spaces.
- Line length: 79 characters in PEP 8; many projects choose 88 (Black's default) or 99–120. Pick one and apply it everywhere.
- Blank lines: two around top-level functions and classes, one between methods.
- Spaces around assignment and comparison operators (
x = y + 1), after commas, but not inside brackets (spam(ham[1], {eggs: 2})) or before a call's opening bracket (print(x), notprint (x)). - No spaces around
=for keyword arguments and defaults:def book(lane, time=None):andbook(3, time="18:00").
# Avoid
total=price*qty+fee
result = calculate( lanes [0] , fee = 5 )
# Prefer
total = price * qty + fee
result = calculate(lanes[0], fee=5)Avoid aligning code with extra spaces to make columns line up; adding one longer name forces you to realign every line, and diffs become noisy.
Imports and layout
Put imports at the top of the file, one module per line, in three groups separated by blank lines:
import csv # 1. standard library
from pathlib import Path
import requests # 2. third-party packages
from swimclub.fees import calculate_fee # 3. your own projectWithin each group, sort alphabetically. Prefer explicit imports: from module import * hides where names come from, can silently overwrite your own names, and confuses linters. Absolute imports (from swimclub.fees import ...) are clearer than relative ones in most code.
A typical module is laid out as: docstring, imports, constants, classes and functions, then an if __name__ == "__main__": block. Following that order means readers always know where to look.
Project policy
PEP 8 itself says consistency within a project matters more than consistency with PEP 8, and consistency within a function matters most. It also lists reasons to break a rule, for example to match surrounding code or to stay compatible with older versions.
The practical answer is to let tools decide:
- Black (or
ruff format) reformats code automatically to one style, removing almost all debate about layout. - Ruff (or Flake8 and Pylint) reports naming problems, unused imports, unused variables, likely bugs and much more;
ruff check --fixfixes many automatically, including import sorting. - pre-commit runs these tools on every commit, so badly formatted code never reaches the repository.
Record the choices in pyproject.toml, for example [tool.ruff] line-length = 88, so every editor and CI run uses the same settings.
Independent practice and a worked check
Task: refactor an enrolment script for meaningful names, consistent whitespace and clear imports without changing behaviour.
# Original:
# import sys,math
# def f(c,e): return math.ceil((c-e)/2)
# print(f(24,17))
import math
def lanes_needed(waiting_swimmers, per_lane):
"""Return the number of extra lanes for the waiting list."""
return math.ceil(waiting_swimmers / per_lane)
capacity = 24
enrolled = 17
print(lanes_needed(capacity - enrolled, per_lane=2)) # 4, same as beforeThe unused sys import is removed, names explain the calculation, and the output is unchanged. For the worked check, Python functions are commonly named in snake_case.
Reference and next practice
Read PEP 8 itself; it's shorter than you might expect. The Black and Ruff documentation explain configuration. Continue with type checking and revisit naming and assignment. Practise with the Python exercises and Python quiz.
Code Style and PEP 8 Summary
- Readable naming: snake_case names normally identify functions and variables, while class names commonly use CapWords.
- Whitespace: Consistent indentation and spacing help reveal program structure.
- Imports and layout: Imports are normally organized near the top and separated into sensible groups.
- Project policy: A project can adopt formatter and lint rules that make its chosen conventions consistent.
Code Style and PEP 8 Examples
Each example shows the code, the output it produces and what each part does.
Code Style and PEP 8 in a small program
def remaining_places(capacity, enrolled):
return capacity - enrolled
available_places = remaining_places(24, 17)
print(available_places)7Line-by-line explanation
- Line 1A
snake_casefunction name and parameter names that state what they hold. - Line 4The variable name says what the number means, so no comment is needed.
Refactoring an enrolment script
Before: def CalcFee(a,Junior=False):F=12;return F*a*(0.5 if Junior==True else 1). After applying PEP 8 and clearer names:
BASE_FEE = 12
JUNIOR_DISCOUNT = 0.5
def calculate_fee(sessions, is_junior=False):
"""Return the total fee for a number of swimming sessions."""
rate = BASE_FEE * (JUNIOR_DISCOUNT if is_junior else 1)
return sessions * rate
class SwimClass:
def __init__(self, name, capacity):
self.name = name
self.capacity = capacity
self.swimmers = []
def is_full(self):
return len(self.swimmers) >= self.capacity
print(calculate_fee(4))
print(calculate_fee(4, is_junior=True))
print(SwimClass("Dolphins", 2).is_full())48
24.0
FalseLine-by-line explanation
- Lines 1 to 2Magic numbers become named constants.
- Line 5
snake_casenames, and a boolean parameter named as a question. - Line 7
if is_juniorreplaces the redundant== Truecomparison. - Line 11Classes use
CapWords; two blank lines separate top-level definitions.
Common Code Style and PEP 8 Mistakes
Comparing booleans and None with ==
if has_paid == True:
...
if coach == None:
...if has_paid:
...
if coach is None:
...PEP 8 recommends testing booleans directly and using is for None.
Wildcard imports
from swimclub.fees import *
fee = calculate_fee(4) # where did calculate_fee come from?from swimclub.fees import calculate_fee
fee = calculate_fee(4)Code Style and PEP 8 in Real Projects
Style in real projects
Almost every serious Python project runs a formatter and a linter in continuous integration, so code style is enforced automatically and reviewers can focus on behaviour. Black's "any colour you like, as long as it's black" approach is popular precisely because it ends formatting arguments. Ruff has become widely adopted because it combines dozens of linters and runs extremely fast.
When you contribute to an existing project, follow its configured style even where it differs from PEP 8. When you start a new one, adding Black or ruff format, Ruff and pre-commit on day one costs a few minutes and saves hours of review comments.
Code Style and PEP 8 FAQ
What is PEP 8?
PEP 8 is the "Style Guide for Python Code", a Python Enhancement Proposal written in 2001 and updated since. It sets conventions for naming, indentation, whitespace, line length, imports and comments, and is followed by most Python projects.
Is the PEP 8 line length 79 or 88?
PEP 8 recommends 79 characters for code and 72 for comments and docstrings, while allowing teams to agree on up to 99. Black uses 88 by default, and many projects adopt that. What matters most is that the whole project uses one limit.
Should I use Black or Ruff?
They complement each other. Black, or Ruff's formatter, rewrites layout automatically. Ruff's linter finds problems such as unused imports, undefined names and naming violations. Many projects use ruff format and ruff check together.
Does PEP 8 require type hints?
No. PEP 8 covers layout and naming and only gives formatting advice for annotations, such as spaces around ->. Whether to use type hints is a separate decision; see the type checking lesson.
Do I have to follow PEP 8?
Python runs code regardless of style, so it's not required. But following it makes your code easier for others to read and is expected in most teams and open-source projects. When a project has its own style, follow that.
Code Style and PEP 8 Exercises
Answer each one and check it instantly. If you get stuck, open the hint before the answer.
Which style commonly names Python functions?
Which review concern is valid for code style and pep 8?
Code Style and PEP 8 Quiz
2 questions to check what you learned.
Which style commonly names Python functions?
- CapWords
- snake_case
- Python ignores the distinction
Which statement correctly describes imports and layout?
- Imports are normally organized near the top and separated into sensible groups.
- Only the example output matters, not the rule
- Every operation has the same behavior