Functions, *args, **kwargs
Theme
Palette
Download
Caption (tap to copy)
📖 Deep dive (full written explanation)
This cover opens the mechanics post by promising to demystify the parts of *args and **kwargs that feel like magic: ordering rules and the pack-versus-unpack duality. The framing — that the rules stop being mysterious once you watch Python match a call slot by slot — sets a precise, machine-level tone.
The magnifying-glass framing marks this as the engine-room post. Where the concept post gave the shape and the why post gave the motivation, this one delivers the exact rules that let you predict how any call resolves against any signature, including why the dreaded 'multiple values for argument' error appears.
The parameter-ordering rule is the foundation everything else rests on. A legal def line lists positional-or-default parameters first, then *args, then keyword-only parameters, then **kwargs. You cannot place a regular parameter after *args or anything after **kwargs. This strict order is not arbitrary — it's what makes argument routing unambiguous.
Understanding the order as a consequence of the routing problem makes it stick. Python needs to know exactly where each argument should go, and a fixed slot order is what guarantees there's never ambiguity about whether a value is positional, named, or overflow. Internalizing this order prevents the syntax errors that come from putting parameters in the wrong place.
The flow diagram lays out the legal signature order as four ordered boxes: positional/default, then *args, then keyword-only, then **kwargs last. Seeing them as a fixed sequence reinforces that this isn't a style preference but a syntactic requirement Python enforces.
The diagram also previews the keyword-only slot — the parameters that sit between *args and **kwargs — which the post develops later. Visualizing the full legal layout once gives the reader a template to check any signature against: if the parts appear in this order, it's valid; if not, Python will reject it before the function ever runs.
This slide explains packing on the definition side: one star gathers leftover positional values into a tuple, two stars gather leftover named values into a dict. The ordering of the gathering matters — Python fills the explicitly named parameters first, and only what's left overflows into *args and **kwargs.
The 'named first, then overflow' rule is the key mechanic. It explains why, given def f(a, b, *args), calling f(1, 2, 3) puts 1 and 2 into a and b and only 3 into args. Grasping that the fixed parameters have priority and the stars catch the remainder makes the routing of any mixed call predictable.
This code slide proves the packing rule with a signature that uses all the pieces: f(a, b, *args, **kwargs). The call f(1, 2, 3, 4, x=5, y=6) routes 1 and 2 to a and b, the extra positional 3 and 4 into the args tuple, and the named x and y into the kwargs dict. The printed output makes every destination explicit.
Running this and then varying the call — adding more positional values, more keywords, or fewer — is the fastest way to feel the routing algorithm from the inside. The slide is essentially the flow diagram from the concept post turned into runnable code, confirming that the routing intuition matches reality.
This slide reveals the duality at the heart of the day: the same stars do the OPPOSITE job at a call site. One star spreads a list or tuple into separate positional arguments; two stars spread a dict into keyword arguments. f(*[1, 2]) is literally f(1, 2). Pack in a def, unpack in a call — same symbol, mirror operation.
This mirror is the concept most learners miss, and naming it directly is the post's most valuable contribution. Once you hold 'stars gather on the def side and spread on the call side,' the forwarding pattern in wrappers — func(*args, **kwargs) — reads correctly: you're unpacking the tuple and dict back into the individual arguments the inner function expects.
The code demonstrates unpacking on the call side. A plain three-parameter point function is called first with *coords, spreading a list into the three positional arguments, and then with **opts, spreading a dict into the three keyword arguments. Both produce the identical result, showing the two unpacking forms side by side.
The lesson is that unpacking lets you bridge data structures and function calls. When your arguments already live in a list or dict — from parsing, configuration, or another function's return — the stars spread them into the call without manual indexing. This is the call-side complement to the packing the previous slides covered, and a workhorse pattern in real code.
The compare diagram puts packing and unpacking next to each other to cement the duality. The left column shows the def-side behavior — many values collapse into one tuple, many named into one dict, gathering the leftovers. The right shows the call-side behavior — one tuple spreads into many values, one dict into many named, spreading them out.
Seeing both columns aligned makes the symmetry impossible to miss: the operations are exact inverses distinguished only by where the stars appear. This single diagram is the antidote to the most common confusion in the topic, and it directly sets up the closing mistake about confusing the two directions.
This slide introduces keyword-only arguments, a powerful but underused feature. Anything written after *args — or after a bare * — must be passed by name, never by position. def f(a, *, mode) forces callers to write f(1, mode='x'). The bare star marks the boundary where positional passing ends.
This matters because it lets library authors make important options explicit and impossible to set accidentally in the wrong positional slot. It's the mechanism behind the 'caught typo' fix in the mistakes post: a keyword-only parameter with a fixed name rejects misspelled or misplaced arguments loudly instead of silently swallowing them into **kwargs.
This code slide demonstrates keyword-only enforcement. The save function uses a bare * to make overwrite keyword-only. Calling save('file', overwrite=True) succeeds, but save('file', True) raises a TypeError because Python refuses to fill a keyword-only parameter positionally. Catching and printing the error makes the rule tangible.
The practical value is forcing clarity at the call site. A bare True conveys nothing about what it controls; overwrite=True is self-documenting. Keyword-only parameters trade a little caller convenience for readability and safety, which is exactly the tradeoff well-designed APIs make for their most consequential options.
The tips slide consolidates the post's mechanics into five recallable rules: the legal order is positional, *args, keyword-only, **kwargs; the star packs in a def and spreads in a call; positional leftovers go to a tuple; keyword leftovers go to a dict; and everything after a bare star is keyword-only.
Together these rules let you predict any call's resolution by inspection — which arguments land where, what's legal, and what will error. That predictive power is the real deliverable of the mechanics post: not just using *args and **kwargs, but reasoning about them with confidence before running the code.
The closing mistake targets the duality confusion head-on: the same star means opposite things depending on location. In a def, *args COLLECTS; in a call, *seq SPREADS. People intuitively expect that writing func(*args) inside a wrapper passes the tuple as a single argument, when it actually unpacks the tuple back into separate arguments.
The cure is simply knowing which side of the operation you're on. If you're writing a def line, the stars gather; if you're writing a call, the stars spread. Holding that one distinction resolves nearly every confusion about forwarding, and it directly motivates the 'forgetting the stars' bug that headlines the code and mistakes posts.
This CTA moves from theory to practice. Having built the precise mental model of packing, unpacking, ordering, and keyword-only arguments, the reader is ready to drill the concrete patterns that turn that understanding into fluency.
The teaser promises a hands-on, code-heavy tour of the patterns you'll actually type week to week — variadic functions, transparent wrappers, default merging, and forwarding — which is exactly what the Code Example post delivers.