✎ Edit content·DAY 018 · POST 5 OF 5 · Common Mistakes

Functions, *args, **kwargs

Python · 13 slides
DAY 018 · POST 5 OF 5
(REMINDER)
DAY 018
args & *kwargs Mistakes
@saurav_dnj_24github.com/SauravDnj · linkedin.com/in/sauravdnj
1 / 13

Theme

Palette

Download

4K — sharpest, slowest
🎬 Video options
Preparing preview…
Live preview · loops the “none” effect
All rendering runs in your browser. No server, no cost, no upload. MP4/WebM = full motion + effects · GIF = lightweight loop · PNG/PDF = static for the Instagram & LinkedIn carousel.

Caption (tap to copy)

📖 Deep dive (full written explanation)

The slides stay clean and scannable. Here's the in-depth explanation behind each one — great for the blog version, show notes, or studying the topic properly.
Slide 1 · *args & **kwargs Mistakes

This cover frames the final angle as a field guide to failure modes. The hook makes the key point: the same flexibility that makes *args and **kwargs elegant is also what hides bugs when they're misused. Powerful, open-ended features fail in quiet ways that no traceback announces.

The emphasis on 'most of these bugs aren't syntax errors' is deliberate. Beginners expect mistakes to crash loudly, but the worst problems here are silent: state shared across calls, arguments arriving as the wrong type, typos swallowed without complaint, and signatures so vague they pass review while telling no one anything. Anticipating these is what separates clever code from production code.

Slide 2 · 1. The mutable default trap

The first mistake is the infamous mutable default argument. A default value like items=[] is evaluated exactly once, when the function is defined — not on each call. So every call that relies on the default shares the same list object, and mutations persist across calls. The 'empty' default quietly accumulates state.

The fix is the None sentinel pattern: default the parameter to None, then create a fresh list inside the body when None is seen. This guarantees each call gets its own object. Although this trap isn't unique to *args and **kwargs, it appears constantly in functions with optional collection arguments, which is why it leads the mistakes post.

Slide 3 · Shared default vs fresh default

This code slide makes the mutable default trap and its fix unmistakable. The bad version defaults items to a shared list; calling it twice shows the second call returning [1, 2] because the list survived from the first call. The good version uses None and creates a fresh list each time, so each call returns only its own element.

The contrast is the whole lesson: identical-looking functions behave completely differently because of when the default is evaluated. Running both and seeing the leak in the first is far more convincing than any explanation. The None-sentinel idiom shown here is the standard, idiomatic remedy and worth memorizing as a reflex.

Slide 4 · 2. Forgetting the stars when forwarding

The second mistake is forgetting the stars when forwarding, the same bug flagged at the end of the code post, now examined as a failure mode. Inside a wrapper, func(args, kwargs) passes the tuple and dict as two positional arguments, so the wrapped function receives a tuple and a dict instead of its real arguments — usually causing a TypeError or wrong behavior.

The fix is to keep the stars: func(*args, **kwargs) unpacks the collected tuple and dict back into individual arguments. This is the call-side unpacking from the mechanics post in its most common application. Because the bug is so easy to introduce and so common, recognizing a starless forward on sight is an essential debugging skill.

Slide 5 · No stars vs stars

This code slide shows the wrong and right forwarding side by side, with the wrong version commented out so the snippet still runs. The contrast between func(args, kwargs) and func(*args, **kwargs) is just two stars, but it's the difference between passing two wrong objects and correctly reconstructing the original call.

The commented-out wrong line is a deliberate teaching choice: it puts the mistake right next to the fix without breaking the runnable example. Seeing add(2, 3) correctly return 5 through the properly-starred wrapper confirms that the stars are doing real unpacking work, not decoration.

Slide 6 · 3. 'multiple values for argument'

The third mistake is the 'got multiple values for argument' error, which surfaces when a single parameter receives a value both positionally and by keyword. It most often happens when unpacking a dict with ** into a call where a positional argument already fills one of the dict's keys. Python refuses to assign two values to one parameter.

The rule to internalize is that each value should travel one channel — positional or keyword, never both for the same parameter. This bug is easy to trigger accidentally when mixing explicit arguments with unpacked dicts, so being aware of the overlap between your positional arguments and your dict keys is the practical defense.

Slide 7 · The double-assignment error

This code slide reproduces the double-assignment error concretely. greet is called with 'Bob' positionally filling name, and then **extra also contains a name key, so name receives two values and Python raises a TypeError. Catching and printing the error shows the exact 'multiple values for argument' message a reader will encounter in the wild.

The example is deliberately realistic: unpacking a dict that happens to share a key with a positional argument is a natural mistake when assembling calls dynamically. Seeing the error message paired with its cause makes it instantly recognizable later, turning a confusing traceback into a known, quickly-fixed pattern.

Slide 8 · 4. Hiding the real signature

The fourth mistake is hiding a real signature behind the stars, the failure mode the why post previewed. Writing def process(*args, **kwargs) when the function genuinely needs a path and a mode strips away everything that helps callers: IDE autocomplete, type hints, readable documentation, and clear errors when someone calls it wrong.

The principle is that *args and **kwargs are for genuine pass-through and variadic cases, not a shortcut to avoid naming inputs you already know. When the inputs are fixed and known, naming them is strictly better — it makes the function self-documenting and lets tooling help everyone who uses it. Reserve the stars for when flexibility is actually required.

Slide 9 · 5. **kwargs swallows typos silently

The fifth mistake is the quietest and most dangerous: **kwargs silently swallows typos. If a function accepts **kwargs and a caller misspells an option — timeoutt for timeout — the misspelled key lands harmlessly in the kwargs dict and is ignored, while the function uses its default. No error fires; the bug just produces subtly wrong behavior.

This is the dark side of the API-flexibility benefit from the why post. The defenses are to validate the keys you receive against a known set, or better, to use explicit keyword-only parameters when the option set is fixed, so misspellings raise a TypeError immediately. Flexibility and safety trade off here, and the choice should be deliberate.

Slide 10 · Silent typo vs caught typo

This code slide contrasts the silent and caught behaviors directly. The loose function accepts **kwargs and reads timeout with a default; calling it with the typo timeoutt=5 returns 30, the default, because the typo was silently ignored. The strict function uses a keyword-only timeout, so the same typo raises a TypeError that's caught and printed.

The comparison crystallizes the tradeoff: **kwargs is forgiving to the point of hiding errors, while explicit keyword-only parameters are strict and catch mistakes at the call. When the set of options is known and fixed, the strict form is almost always safer. Seeing the typo pass silently in one case and fail loudly in the other makes the case better than any explanation.

Slide 11 · Stars or named params?

The decision diagram distills the day's central judgment into a flowchart. First: do you know the inputs up front? If yes, name them explicitly. If not, are you forwarding to another call? If so, use *args/**kwargs pass-through. Otherwise, if the function is genuinely variadic use *args, and if not, name the parameters anyway.

This single chart captures the two most important rules from the whole day: explicit parameters are the default, and the stars are earned by genuine forwarding or variadic needs. Running any candidate function through these questions reliably steers you toward a clear, well-typed signature and away from the vague (*args, **kwargs) that hides everything.

Slide 12 · Stay out of trouble

The tips slide consolidates all five mistakes into a defensive checklist: never use a mutable default — use None; keep the stars when forwarding with func(*args, **kwargs); send each value through one channel, positional or keyword; don't hide a known signature behind **kwargs; and validate kwargs keys or prefer keyword-only parameters. Each bullet maps to one failure mode from the post.

Designed as a final reference, this card lets a reader audit any function they write against the known traps. Together with the patterns card from the code post, it forms a complete practical toolkit — the patterns tell you what to write, and these tips tell you what to avoid.

Slide 13 · Save this. Follow for Day 19.

This CTA closes both the mistakes post and the full five-post arc on functions, *args, and **kwargs. The reader now has the concept, the motivation, the mechanics, the patterns, and the pitfalls — a complete picture of when and how to use variadic and keyword arguments well.

The teaser points forward to the next entry in the series, keeping the momentum of the 100 Days of AI sequence while signaling that flexible function signatures are now a tool the reader can deploy confidently as the Python foundation continues to build.

🎨 AI image prompt (matches this theme + palette)

Paste into Midjourney, DALL·E, Ideogram, etc. to generate an on-brand image, then upload it on the Edit content page. The prompt updates automatically with the selected theme + palette.