*args & **kwargs
How does print() accept one argument or ten? How does a function pass along whatever it's given to another function? The answer is *args and **kwargs — Python's way of writing functions that accept a flexible, unknown number of inputs.
Learn *args & **kwargs in our free Python course — a beginner-friendly interactive lesson with runnable examples, a practice exercise and a quick recall.
Part of the free Python course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.
Once you understand the star operators, a lot of "magic" library code suddenly reads clearly.
What You'll Learn in This Lesson
1 The Problem *args Solves
Imagine writing an add function. With fixed parameters you'd have to guess how many numbers the caller might pass:
*args fixes this. The star tells Python: "gather all the leftover positional arguments into a single tuple called args ":
Inside the function, args is just a normal tuple — you can loop over it, index it, or pass it to sum() .
🏷️ **kwargs — Flexible Named Arguments
While *args catches extra positional arguments, **kwargs catches extra keyword (named) arguments and stores them in a dictionary:
Each key=value the caller supplies becomes one entry in the kwargs dictionary. This is exactly how flexible configuration functions and many web framework APIs work under the hood.
2 The Required Parameter Order
You can combine regular parameters, *args , and **kwargs — but only in one specific order. Get it wrong and Python raises a SyntaxError :
3 The Other Direction: Unpacking at the Call Site
The star operators work in reverse too. When you call a function, * spreads a list/tuple into positional arguments and ** spreads a dict into keyword arguments:
🔁 The Classic Use: Forwarding Arguments
The most common professional reason to use both together is writing a wrapper that accepts anything and passes it straight through to another function — the heart of how decorators work:
Because timed doesn't care what arguments area needs, it can wrap any function at all. This "accept anything, forward everything" pattern is everywhere in real Python libraries.
🧩 Reorder Challenge
These lines should define an averaging function and call it by unpacking a list. Reorder them so the output is Average: 20.0 .
Why: the def header (B) must come before its indented body (A). The values list (C) is created next, then the call (D) unpacks it with *values so the three numbers arrive as separate positional args. Their average is 20.0.
🧠 Quick Recall
<class 'tuple'> — *args always collects extra positional arguments into a tuple, even though you call with a star.
7 — *[10, 3] spreads the list so a=10 and b=3 , giving 10 - 3 .
{'a': 9, 'b': 2} — merging dicts with ** lets later keys overwrite earlier ones, so a becomes 9.
📋 Star Operator Quick Reference
Syntax
Where
Does
*args
Definition
Collect extra positionals → tuple
**kwargs
Collect extra keywords → dict
*list
Call site
Spread list → positional args
**dict
Spread dict → keyword args
{**a, **b}
Literal
Merge two dictionaries
Frequently Asked Questions
🎯 Mini Challenge: Flexible Shopping Cart
Write a checkout function that totals any number of prices and accepts optional named settings.
Lesson complete — your functions are flexible now!
You can collect extra arguments with *args and **kwargs , order parameters correctly, unpack collections at the call site, and forward arguments through wrappers. This unlocks a huge amount of real-world Python code.
🚀 Up next: Lambda Functions — tiny one-line functions for sorting, mapping, and filtering.
Practice quiz
Inside def f(*args), what type is args?
- list
- dict
- tuple
- set
Answer: tuple. *args always collects extra positional arguments into a tuple.
Inside def g(**kwargs), what type is kwargs?
- dict
- tuple
- list
- set
Answer: dict. **kwargs collects extra keyword arguments into a dictionary.
Given def add(*args): return sum(args), what does add() return?
- None
- an error
- ()
- 0
Answer: 0. With no arguments, args is the empty tuple () and sum(()) is 0.
Given def sub(a, b): return a - b, what does sub(*[10, 3]) print?
- 13
- 7
Answer: 7. *[10, 3] spreads the list so a=10 and b=3, giving 10 - 3 = 7.
What is the result of {**{'a': 1}, **{'a': 9, 'b': 2}}?
- {'a': 9, 'b': 2}
- {'a': 1, 'b': 2}
- {'a': 10, 'b': 2}
- an error
Answer: {'a': 9, 'b': 2}. When merging dicts with **, later keys overwrite earlier ones, so a becomes 9.
Which parameter order is valid in a function signature?
- def f(**kwargs, *args)
- def f(*args, a, **kwargs)
- def f(a, *args, **kwargs)
- def f(*args, a)
Answer: def f(a, *args, **kwargs). The required order is: normal params, then *args, then keyword-only params, then **kwargs.
In a function DEFINITION, what does the * in *args do?
- Spreads a tuple into arguments
- Collects extra positionals into a tuple
- Multiplies the arguments
- Nothing
Answer: Collects extra positionals into a tuple. In a definition the star collects; at a call site it spreads. Same symbol, mirror jobs.
A parameter listed AFTER *args in a signature becomes...
- positional-only
- optional
- ignored
- keyword-only
Answer: keyword-only. Anything after *args must be passed by name, making it keyword-only.
What does [*[1, 2], *[3, 4]] evaluate to?
Unpacking with * spreads both lists into one flat list [1, 2, 3, 4].
Why is *args/**kwargs ideal for writing a wrapper or decorator?
- It runs faster
- It hides the original function
- It forwards any arguments straight through to the wrapped function
- It removes default values
Answer: It forwards any arguments straight through to the wrapped function. The wrapper accepts anything and forwards it with func(*args, **kwargs), so it can wrap any function.
Continue this course
- Previous: List, Dict & Set Comprehensions
- Next: Lambda Functions