Part 0 · 2 chapters · ~12 min

Basics

Installing Python and virtual environments (venv, uv), names and objects, numbers, strings and bytes, lists, tuples, dicts and sets, control flow, functions with defaults and keyword arguments, comprehensions, generators, decorators and context managers, modules and packages, and the standard library tour.

1

Names, objects and the data model

code
uv venv && source .venv/bin/activate      # or: python3 -m venv .venv
uv pip install django pytest

def fee(amount_kobo: int, *, rate_bps: int = 50, minimum: int = 1_000) -> int:   # keyword-only after *
    if amount_kobo <= 0:
        raise ValueError(f"amount must be positive, got {amount_kobo}")
    return max(amount_kobo * rate_bps // 10_000, minimum)

fees = [fee(a) for a in (100_000, 5_000_000)]                 # list comprehension → [1000, 25000]
by_owner = {t.owner: t for t in transfers}                    # dict comprehension
big = (t for t in transfers if t.amount > 10_000_000)         # generator: lazy, one item at a time
EVERYTHING IS AN OBJECT
names point at objects; objects carry their type and a reference count
name: balancename: alsoint objectvalue 500000, refcount 2, type intlist objectmutabletuple objectimmutable
swipe the figure sideways, or tap expand for full screen
1/4
names and objects
Variables are names bound to objects, not boxes holding values. balance = 500_000 binds the name to an int object.
names bind to objectsnot boxes holding values
2

Generators, decorators and context managers

code
def read_statement(path):                       # a generator: constant memory over a huge file
    with open(path, encoding="utf-8") as f:     # context manager: closed even on exceptions
        for line in f:
            yield parse(line)

import functools, time
def timed(fn):                                  # a decorator wraps a function
    @functools.wraps(fn)
    def wrapper(*args, **kwargs):
        t = time.perf_counter()
        try: return fn(*args, **kwargs)
        finally: log.info("%s took %.1f ms", fn.__name__, (time.perf_counter() - t) * 1000)
    return wrapper

@timed
def reconcile(day): ...
money
Never use float for money: 0.1 + 0.2 == 0.30000000000000004 in Python too. Use integer minor units, or decimal.Decimal with an explicit context and rounding mode (Computers part 7).