Module 9 · Idiomatic & Best Practices ⏱ 18 min

PEP 8 & Code Style

By the end of this lesson you will be able to:
  • Apply PEP 8's core layout rules: four-space indentation, line length, and blank lines between definitions
  • Choose identifiers that follow Python's naming conventions for functions, classes, and constants
  • Recognise and fix the tab-and-space indentation errors that crash a program before it runs

Code is read far more often than it is written. The line you type today will be read by a teammate tomorrow, by you in six months, and by a reviewer deciding whether it is safe to ship. Python gives you enormous freedom — you may name a variable x, X, or data_2 — and that very freedom is why a shared style matters.

PEP 8 is Python's official style guide. It is not part of the language; the interpreter runs ugly code perfectly well. PEP 8 is a set of agreements about how to lay code out so that any Python programmer can absorb it at a glance. Following it is what separates a script that merely works from a program a whole team can maintain.

This lesson covers the conventions you will meet every day: indentation, line length, blank lines, imports, and the naming rules that turn identifiers into documentation.

flowchart TD
  R["Readable code"] --> A["Layout"]
  R --> B["Naming"]
  R --> C["Whitespace"]
  R --> D["Structure"]
  style R fill:#3776ab,color:#fff
  style A fill:#1e293b,color:#fff
  style B fill:#1e293b,color:#fff
Readable code rests on four facets — and PEP 8 has a rule for each.

Four spaces, consistently

The rule you feel first is indentation. PEP 8 prescribes four spaces per level and asks that a file use one indent character throughout. Python itself is stricter still: mixing tabs and spaces in a single block raises a TabError before a line of your code runs. Consistent indentation is therefore not just style — it is the prerequisite for execution.

Line length is the other layout rule people argue over. PEP 8 suggests 79 characters as a limit, with a relaxed 99 acceptable for teams that prefer it. The exact number matters less than the habit: a line so long it wraps unpredictably on a reviewer's screen is hard to read and hard to discuss. When an expression grows long, split it inside parentheses rather than at an arbitrary point.

Four-space indentation in a small, readable function. Press Run.
def weekly_pay(hours, rate):
    """Return gross pay for a week, including overtime."""
    if hours > 40:
        overtime = (hours - 40) * rate * 1.5
        return 40 * rate + overtime
    return hours * rate

print(weekly_pay(40, 20))
print(weekly_pay(45, 20))

Names that explain themselves

PEP 8's naming rules pay off every single day, because good names make code self-documenting. Three shapes cover almost everything you will write:

  • snake_case for functions, methods, and variables — total_price, user_count.
  • PascalCase for classes — ShoppingCart, NetworkClient.
  • UPPER_CASE for constants — MAX_RETRIES, DEFAULT_TIMEOUT.

The shape tells you what a name is before you read the line it sits on. When you meet MAX_RETRIES = 3, the capitals announce that this value does not change; when you write client = NetworkClient(), the capital in the class and the lowercase in the variable line up exactly as a reader expects.

Favour whole words, too. usr_cnt saves four keystrokes and costs a reader a beat of decoding every time it appears. user_count costs nothing and reads instantly.

flowchart LR
  subgraph V["snake_case"]
    V1["functions & variables"] --> V2["total_price"]
  end
  subgraph C["PascalCase"]
    C1["classes"] --> C2["ShoppingCart"]
  end
  subgraph U["UPPER_CASE"]
    U1["constants"] --> U2["MAX_RETRIES"]
  end
  style V2 fill:#3776ab,color:#fff
  style C2 fill:#b45309,color:#fff
  style U2 fill:#1e293b,color:#fff
Three name shapes, each reserved for one kind of identifier.
All three naming conventions in one short module.
MAX_CAPACITY = 10

class Cart:
    def __init__(self):
        self.items = []

    def add(self, product):
        if len(self.items) < MAX_CAPACITY:
            self.items.append(product)

cart = Cart()
cart.add("book")
cart.add("lamp")
print(len(cart.items))

Whitespace and breathing room

Around operators, PEP 8 asks for a single space on each side — total = price + tax, never total=price+tax — and no space directly inside brackets, so items[0] rather than items [0]. These rules look petty in isolation, yet a file where every operator is spaced identically reads as one calm voice instead of visual noise.

Blank lines carry meaning as well. Two blank lines separate top-level functions and classes; one blank line separates methods inside a class. That rhythm is how the eye finds the boundary of a unit of code before it reads a single word of it.

Imports at the top

PEP 8 places every import at the top of the file, one per line, in a fixed order: the standard library first, then third-party packages, then your own modules, with a blank line between groups. Gathering imports in one predictable place means a reader can see everything a module depends on before any logic begins.

Sorting them alphabetically within each group keeps changes tidy: when two people each add an import, the lines land in a stable position rather than wherever each person happened to type.

flowchart TD
  F["a clean .py file"] --> S1["module docstring"]
  F --> S2["imports<br/>stdlib then third-party"]
  F --> S3["constants"]
  F --> S4["classes & functions"]
  style F fill:#3776ab,color:#fff
A clean Python file has a predictable top-to-bottom layout.
Imports, a constant, and a function — in PEP 8 order, spaced apart.
import math

PI_APPROX = math.pi


def circle_area(radius):
    return PI_APPROX * radius ** 2


print(circle_area(2))
Exercise

Write average_speed(distance_km, time_hours) that returns the average speed. Use a clear snake_case name and a descriptive parameter for each value, and return rather than print.

def average_speed(distance_km, time_hours):
    # distance over time
    pass
Exercise

A constant defined at the top of a module. Predict what this prints.

MAX_SCORE = 100
score = 75
print(score <= MAX_SCORE)
Exercise

This function is meant to return the total of a list of prices, but it crashes with an IndentationError before it runs — the body is not indented under the def. Fix the indentation so it runs.

def total_price(prices):
prices_total = sum(prices)
    return prices_total
Exercise

Write format_price(amount) that returns a price string with a currency sign and exactly two decimals, so format_price(9.5) returns "$9.50".

def format_price(amount):
    # build a string like "$9.50"
    pass
Exercise

Which name follows PEP 8 for a variable holding the number of active users?

Recap

  • PEP 8 is Python's style guide — conventions, not law, but following them makes code readable to any Python programmer.
  • Indent four spaces, consistently; mixing tabs and spaces is a TabError, not a warning.
  • Name by shape: snake_case for functions and variables, PascalCase for classes, UPPER_CASE for constants.
  • One space around operators, none inside brackets; two blank lines between top-level definitions.
  • Imports go at the top, grouped: standard library, third-party, local.

Next you will turn these readable building blocks into idiomatic Python — the patterns experienced Python programmers reach for without thinking.

Checkpoint quiz

Which indentation does PEP 8 prescribe?

Which name follows PEP 8 for a class?

Go deeper — technical resources