Visual walkthrough — Functions — def, parameters, return, docstrings
We will use a running example: a machine that adds two numbers. Simple on purpose — the plumbing is the lesson, not the arithmetic.
Step 1 — Start with raw, repeated work
WHAT. Before functions exist, suppose you keep writing the same computation over and over. Say you need the sum of two numbers in three different places in your program.
WHY. We must feel the pain functions cure. The pain is repetition — writing a + b again and again, each copy a place a bug can hide. This is exactly the DRY Principle ("Don't Repeat Yourself") the parent note named.
PICTURE. Three separate copies of the same little calculation, drifting apart over time.

Look at the three orange blocks: identical logic, three chances to make a typo, three edits every time the formula changes. We want one block that everyone points to.
Step 2 — def builds the machine but does NOT run it
WHAT. We wrap the logic once and give it a name.
def add(a, b):
return a + bWHY. The keyword def (short for define) tells Python: "store this block as a machine called add — do not execute it yet." This is the single most misunderstood point: defining ≠ running. At definition time Python only creates the machine and hangs a name-tag on it.
Term by term, on the first line def add(a, b):
def— the keyword that says "a machine definition begins here."add— the name (name-tag) you will later use to call it.(a, b)— the two parameters: empty slots waiting to be filled.:— starts the indented body (the hidden inner workings).
PICTURE. A boxed machine sits on the shelf, labelled add, with two empty input slots and one output chute. Nothing has moved through it yet.

The slots a and b are hollow — they hold no value until someone calls the machine.
Step 3 — Calling fills the slots (parameters ← arguments)
WHAT. We call the machine with real values:
add(2, 3)WHY. A definition alone computes nothing. The parentheses () are the "press the button" action. The values inside — 2 and 3 — are the arguments: the actual snacks-money you drop into the slots. Python binds them to the parameters by position:
- The argument is the value (right side of the arrow) — what you have.
- The parameter is the slot name (left side) — where it lands.
- The arrow is the binding: for the duration of this one call, inside the machine
ameans2andbmeans3.
PICTURE. Two coins labelled 2 and 3 drop into the slots labelled a and b. The slots light up now that they hold something.

Step 4 — The body runs, return sends the answer out
WHAT. Inside, with a = 2 and b = 3, Python runs the body and hits return a + b.
WHY. The body does the work, but work done inside the machine is trapped inside unless we hand it back. return does two jobs at once:
- It computes and sends the value
a + b = 5back out the chute to whoever called. - It exits the machine immediately — nothing after
returnon that path runs.
The whole expression add(2, 3) becomes 5 — as if you had typed 5 there.
PICTURE. The value 5 slides out the chute; the machine's inner slots go dark again (the call is over, a and b are forgotten).

Step 5 — Capturing the output vs letting it fall on the floor
WHAT. We usually catch the returned value in a variable:
result = add(2, 3) # result is now 5WHY. The call evaluates to 5; the = assignment stores that 5 under the name result. If you do not assign it and do not use it, the 5 simply drops on the floor and is lost.
PICTURE. The 5 from the chute is caught in a labelled cup result. Beside it, an un-caught 5 splashes on the ground — computed, then wasted.

Step 6 — The degenerate case: no return → None
WHAT. What if the machine has no return (or only print)?
def show(a, b):
print(a + b) # displays, does NOT hand backWHY. print lights a lamp for a human to read the screen — it sends nothing back down the chute. So x = show(2, 3) first shows 5 on screen, but the chute delivers the special empty value ==None==. This is the parent note's print ≠ return trap, seen physically: the lamp is not the chute.
PICTURE. The lamp glows 5 on top of the machine (for your eyes), while the output chute delivers a hollow None token into the cup.

Step 7 — Returning two things at once (a tuple)
WHAT. A machine can hand back several values by separating them with a comma:
def divmod2(a, b):
"""Return (quotient, remainder)."""
return a // b, a % b
q, r = divmod2(17, 5) # q = 3, r = 2WHY. return a // b, a % b packs both results into a single package — a tuple (3, 2) (see Lists and Tuples). One call, one chute, but the package holds two items. On the left, q, r = ... unpacks the package, dropping each item into its own cup.
a // b— integer division: how many whole5s fit in17→3.a % b— remainder: what is left over →2.- the comma between them — the packing that makes it a tuple.
PICTURE. One chute delivers a bundle (3, 2); the bundle splits into two cups q and r.

Step 8 — The docstring: a sticker on the machine
WHAT. The first string literal right under def is the docstring:
def add(a, b):
"""Return the sum of a and b."""
return a + bWHY. Six months later, "what does add do?" is answered instantly by help(add). The docstring is documentation that travels inside the machine itself — a sticker glued to the box, not a note on a separate scrap of paper that gets lost.
PICTURE. The finished machine with a plum sticker across its front reading "Return the sum of a and b."

The one-picture summary
Every idea in one diagram: arguments drop into parameter slots → the body runs → return sends one value (or a tuple) out the chute → the caller catches it, or gets None if there was no return.

Recall Feynman: retell the whole walkthrough
You got tired of writing the same three lines everywhere, so you built a machine once and stuck a name-tag on it with def — but building it doesn't run it, it just sits on the shelf. The machine has empty slots (parameters a, b). When you call it with parentheses, you drop coins (arguments 2, 3) into those slots; now inside the machine a is 2 and b is 3. The body does its hidden work and return drops the answer 5 out the chute — and slams the door (nothing after return runs). You catch that 5 in a cup called result; if you don't catch it, it splashes on the floor. If the machine only lights a lamp with print and never uses the chute, it hands you an empty token, None. It can even drop a bundle out the chute — return q, r — and you split the bundle into two cups. Finally, a sticker (docstring) on the front tells anyone what the machine is for. Build once, use a thousand times.
Connections
- Variables and Scope — the filled slots
a,blive only inside the machine's private scope. - Control Flow — if statements — the body can branch to different
returns. - Lists and Tuples — Step 7's multi-return is tuple packing/unpacking.
- Recursion — a machine whose body calls itself.
- Lambda and Higher-order Functions — the same slot/chute idea, one line, no name.
- DRY Principle — Step 1's pain is exactly what this whole picture cures.