Visual walkthrough — Default parameters, keyword arguments
Step 1 — A function is a row of empty boxes
WHAT. When you write def greet(name, greeting):, Python reserves two labelled boxes — one named name, one named greeting. These names are the parameters: the labels on the boxes in the definition.
WHY start here. Every rule later ("defaults go right", "positionals come first") is really a rule about how boxes get filled. If you picture the boxes, the rules stop being arbitrary.
PICTURE. Two empty boxes waiting. Nothing is in them yet — a bare definition fills nothing.

Step 2 — Positional filling: values fall in left to right
WHAT. Call greet("Asha", "Hi"). Python pours the arguments into the boxes in order: the first value into the first box, the second into the second.
Each label under the code says which box that value lands in. Position — nothing else — decides.
WHY. This is the default behaviour Python always tries first. It is fast and needs no names, but it has a weakness we will expose in Step 4.
PICTURE. Two arrows, first value to box 1, second to box 2, strictly by order.

Step 3 — A default is a value pre-printed inside a box
WHAT. Now write def greet(name, greeting="Hello"):. The second box arrives with "Hello" already printed inside it. That printed value is the default.
name— the term left of the=is the box label."Hello"— the value right of the=is what sits in the box if the caller drops nothing there.
WHY. This is the whole point of defaults (the DRY idea): the common value lives in one place. Callers who are happy with "Hello" say nothing; only the unusual caller overwrites it.
PICTURE. Box 1 empty and required; box 2 already showing a faint "Hello" that a real argument can paint over.

Step 4 — Why position alone fails, and keywords rescue it
WHAT. Give a function three boxes: def box(width, height, x=0, y=0):. Read the call box(2, 5, 0, 1). Which number is which? Position forces you to remember an invisible order.
A keyword argument fixes this: you name the box out loud — x=0 means "put 0 in the box labelled x, wherever it is."
Because each value carries its own label, order stops mattering: box(height=5, width=2) fills the exact same boxes.
WHY this tool and not another. Position is compact but fragile — one slipped value and every later value is wrong, silently. Naming trades a few keystrokes for calls that are self-documenting and order-independent. That is the entire justification for keyword arguments.
PICTURE. Left: four bare numbers with question marks over the boxes. Right: the same call written with names, each arrow landing on its named box with certainty.

Step 5 — The skip trick: reach a far box without touching the near ones
WHAT. With def power(base, exp=2, mod=None):, suppose you want to set mod but leave exp at its default 2. Positionally impossible — to reach the 3rd box by position you must first pour something into the 2nd. Keyword to the rescue:
5— positional, lands inbase.mod=7— named, leaps overexp, soexpkeeps its printed default2.
Result: , then .
WHY. This is the day-to-day payoff of defaults + keywords together: change only the one knob you care about and let every other box keep its factory value.
PICTURE. Three boxes; an arrow to base, a curved arrow hopping over exp to land on mod, with exp still glowing its default 2.

Step 6 — The two iron rules, seen as box logic
WHAT. Two rules, each obvious once you see the boxes.
WHY Rule 1. Python fills by position left to right. If a pre-filled box sat before an empty required box, a single positional value would be ambiguous — does it overwrite the default, or is it meant for the required one behind it? Banning that ordering deletes the ambiguity.
WHY Rule 2. The moment you write c=9, Python stops counting positions. A later bare 2 now has no known slot. So every position-based value must be resolved first.
PICTURE. Top row: a legal definition (empty boxes then pre-filled boxes) with a green check; a broken one with a red cross where a bare value can't tell which box it belongs to.

Step 7 — The degenerate case: when the default is a mutable box-filler
WHAT. Here is the subtle one. The value printed in a default box is created once, the instant the def line runs — not fresh on each call. If that value is a list (a mutable object, one you can change in place), every call that omits the argument shares the same list.
def add(item, bag=[]): # this [] is made ONCE, at def time
bag.append(item)
return bag
add(1) # [1]
add(2) # [1, 2] <- same list, still holding the old item!WHY it happens. The printed default is a single object. append mutates that one object, so the "leftovers" survive into the next call. This is why the concept only bites with mutable defaults — a number or string can't be mutated in place, so it never leaks.
PICTURE. One list object drawn once at definition; three separate calls all pointing arrows at the same box, each append piling on top of the last.

The one-picture summary
Everything above collapses to one flow: arguments in, boxes fill by position, then by name, defaults cover the gaps — and beware the shared mutable box.

Recall Feynman: tell the whole walkthrough plainly
A function is a row of labelled boxes. When you call it, your values fall into the boxes left to right — that's positional. Some boxes come with a value already printed inside (a default), so if you drop nothing there, the printed value stays. When there are many boxes, remembering the order is risky, so you can shout each value's box name — keyword arguments — and now order stops mattering, and you can even skip over a box to fill a far one while leaving the near one at its factory setting. Two rules keep it unambiguous: in the definition, empty required boxes go before pre-filled ones; in the call, all order-based values go before any named ones. And one trap: the printed default is stamped once, so if it's a list you keep adding to, everyone shares the same list — fix it by printing None and building a fresh list inside.
Active recall
Where do arguments land when you call with no names?
What does greeting="Hello" in a def line do?
"Hello" unless the caller overwrites it.How do you set a far box while keeping a nearer default?
power(5, mod=7) — the keyword hops over exp.Why must positional arguments come before keyword ones in a call?
When is a default value created?
Why does the mutable-default bug only hit lists/dicts?
Connections
- Parent topic
- Functions - def and return
- Variable scope - local vs global
- *args and **kwargs (variadic functions)
- Mutable vs immutable objects
- DRY principle - Don't Repeat Yourself