Python Code Style and PEP 8

Write Python that looks like Python: PEP 8 naming, layout and imports, and the tools that apply them automatically.

Lesson 62 of 107Advanced12 min read

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.

Python
# Before
def RemainingPlaces(Cap,enr):return Cap-enr
# After
def remaining_places(capacity, enrolled):
    return capacity - enrolled

This 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

WhatConventionExample
Variables, functions, methods, modulessnake_caseremaining_places, enrolment.py
Classes, exceptionsCapWordsSwimClass, EnrolmentError
ConstantsUPPER_SNAKE_CASEMAX_SWIMMERS
Internal namesleading underscore_validate_age
Clash with a keywordtrailing underscoreclass_, 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), not print (x)).
  • No spaces around = for keyword arguments and defaults: def book(lane, time=None): and book(3, time="18:00").
Python
# 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:

Python
import csv                      # 1. standard library
from pathlib import Path

import requests                 # 2. third-party packages

from swimclub.fees import calculate_fee   # 3. your own project

Within 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 --fix fixes 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.

Python
# 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 before

The 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.

Example 1

Code Style and PEP 8 in a small program

Python
def remaining_places(capacity, enrolled):
    return capacity - enrolled

available_places = remaining_places(24, 17)
print(available_places)
Output
7
Line-by-line explanation
  1. Line 1A snake_case function name and parameter names that state what they hold.
  2. Line 4The variable name says what the number means, so no comment is needed.
Example 2

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:

Python
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())
Output
48
24.0
False
Line-by-line explanation
  1. Lines 1 to 2Magic numbers become named constants.
  2. Line 5snake_case names, and a boolean parameter named as a question.
  3. Line 7if is_junior replaces the redundant == True comparison.
  4. Line 11Classes use CapWords; two blank lines separate top-level definitions.

Common Code Style and PEP 8 Mistakes

Comparing booleans and None with ==

Don't
if has_paid == True:
    ...
if coach == None:
    ...
Do
if has_paid:
    ...
if coach is None:
    ...

PEP 8 recommends testing booleans directly and using is for None.

Wildcard imports

Don't
from swimclub.fees import *
fee = calculate_fee(4)        # where did calculate_fee come from?
Do
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.

Multiple choiceeasy

Which style commonly names Python functions?

Multiple choicemedium

Which review concern is valid for code style and pep 8?

Code Style and PEP 8 Quiz

2 questions to check what you learned.

  1. Which style commonly names Python functions?

    • CapWords
    • snake_case
    • Python ignores the distinction
  2. 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