Functions, *args, **kwargs
Theme
Palette
Download
Caption (tap to copy)
📖 Deep dive (full written explanation)
This cover sets up the most practical post in the day: a code-heavy drill of the *args and **kwargs patterns that recur in real work. The framing is deliberate — reading about the features builds recognition, but typing the patterns builds the muscle memory to write them without hesitation.
The goal across these slides is breadth of pattern rather than depth of theory. By the end the reader should have seen and ideally typed the handful of shapes that cover the overwhelming majority of day-to-day use: variadic inputs, transparent wrappers, default merging, dict unpacking, mixed signatures, and constructor forwarding.
The first pattern is the variadic function: an average that accepts any number of numbers via *nums and computes their mean. The guard clause handles the empty-call case by returning 0 before division, which would otherwise fail. Calling it with two, then five, then zero arguments shows the same function flexing across input counts.
The empty-input guard is worth noting as a habit. Variadic functions must decide what an empty call means — here, zero — and handle it explicitly, because operations like division by len(nums) break on an empty collection. Recognizing that *args can be empty, and planning for it, is part of writing variadic functions that don't crash on edge cases.
This slide builds the transparent wrapper properly, with functools.wraps applied. The logged decorator's inner wrapper accepts (*args, **kwargs), prints a message, then forwards everything with func(*args, **kwargs). It works on multiply without knowing its signature, exactly as the why post promised.
The addition of @functools.wraps over the why post's simpler version is intentional: it copies the wrapped function's name and metadata onto the wrapper so multiply.__name__ stays 'multiply' rather than becoming 'wrapper'. This is the production-quality version of the pattern, and the dedicated functools.wraps slide later explains exactly why it's not optional in real code.
This slide shows the default-merging pattern, the workhorse of configurable functions. make_request starts with a config dict of sensible defaults, then config.update(options) lets any caller-supplied keyword override them. The result is a function with defaults that callers can selectively replace without having to specify everything.
The dict.update approach is cleaner than pulling each key with .get when there are several options, because it expresses 'defaults, then overrides' in one line. Seeing the same function return the pure defaults for a bare call and the merged result when options are passed makes the override semantics concrete and reusable.
This slide demonstrates unpacking at the call site, the call-side mirror from the mechanics post. A connect function with three plain parameters is called first with **settings, spreading a dict into keyword arguments, and then with *args, spreading a list into positional arguments. Both reach the same result.
The practical relevance is that real arguments often already live in a dict or list — loaded from JSON, a config file, or another function's output. Rather than manually pulling out settings['host'], settings['port'], and so on, the double star spreads the whole dict into the call. This bridge between data structures and function calls is one of the most reused patterns in data-heavy code.
The decision diagram gives a quick mental flowchart for choosing the right tool. The first question — are you accepting unknown extras at all? — sends known-input functions to explicit named parameters. If you are accepting extras, the follow-up is whether they're named: yes routes to **kwargs and a dict, no routes to *args and a tuple.
This picker operationalizes the choices scattered through the day into a concrete procedure. In practice the decision is near-instant once internalized, but laying it out explicitly cements when each construct is appropriate and reinforces the central message that the stars are for genuine extras, not for replacing parameters you can name.
This slide assembles the full mixed signature: a fixed title, variadic *rows, a keyword-only sep with a default, and catch-all **meta. The call passes a title, several rows positionally, sep by keyword, and an extra region keyword that lands in meta. The output shows each piece arriving in its proper slot.
This is the capstone of the packing rules from the mechanics post, shown all at once in working code. It demonstrates that the four parameter kinds coexist cleanly when ordered correctly, and that sep being keyword-only (after *rows) means it can't be accidentally consumed as another row. Building one signature that uses every feature ties the day's mechanics together.
This slide shows the most common object-oriented use of **kwargs: forwarding constructor arguments up an inheritance chain. Dog.__init__ takes its own breed parameter plus **kwargs, then calls super().__init__(**kwargs) to pass the remaining arguments — here, name — to the Animal base class. Each class handles only what it owns and forwards the rest.
This pattern is how cooperative inheritance stays maintainable. A subclass doesn't need to know or restate every parameter its parents accept; it captures the extras and forwards them. The example is deliberately minimal, but the same shape scales to deep hierarchies and is standard in frameworks where base classes accept many configuration options.
The tips slide is a compact reference card of the five patterns to commit to memory: variadic input with *nums, the forward-everything wrapper, merging defaults via config.update(kwargs), unpacking a dict with func(**settings), and forwarding up with super().__init__(**kwargs). Each is given in skeletal form for quick pattern-matching.
These five cover the vast majority of practical use. Internalizing them as templates — recognizing 'this is variadic', 'this is a wrapper', 'this needs default merging' — is what makes writing these functions fast. The card is designed to be the thing you glance back at until the shapes become automatic.
This slide elevates functools.wraps from an incidental detail to a named best practice. Without it, wrapping a function silently replaces its name, docstring, and signature with the wrapper's generic ones, which confuses help(), debuggers, tracebacks, and any tool that introspects functions. @functools.wraps copies that metadata back.
The practical rule is simple: any time you write a wrapper that forwards *args and **kwargs, add @functools.wraps(func) to the inner function. It's a single line that makes your decorated functions indistinguishable from the originals to the rest of the ecosystem. Omitting it is a common, quiet source of confusing debugging sessions.
The closing mistake names the single most common wrapper bug: forwarding without the stars. Inside a wrapper, writing func(args, kwargs) passes the tuple and the dict as two ordinary positional arguments — the inner function receives a tuple and a dict instead of the real, spread-out values. You must write func(*args, **kwargs) to unpack them.
This is the practical, code-grounded version of the pack-versus-unpack duality from the mechanics post. The stars on the call side are not decoration; they are the unpacking operation that reconstructs the original call. Recognizing that dropping them changes the meaning entirely — and being able to spot a missing star instantly — is a mark of fluency with the feature.
This CTA closes the hands-on post and points to the final angle: the mistakes that bite everyone. Having built fluency with the patterns, the reader is ready to learn the failure modes that turn elegant wrappers and flexible signatures into silent bugs.
The teaser promises a field guide to what goes wrong, signaling that the mistakes post consolidates the warnings scattered through the earlier angles — mutable defaults, dropped stars, double-assignment, hidden signatures, and swallowed typos — into one defensive checklist.