As a function grows, callers may need to supply values beyond its named parameters. In Python, *args collects extra positional arguments into a tuple, while **kwargs collects extra keyword arguments into a dictionary.
This article explains how to collect and expand those arguments, then how to forward them through another function.
TL;DR: *args and **kwargs in Python
Use *args to collect extra positional arguments and **kwargs to collect extra keyword arguments. At a call site, the same operators expand a tuple or mapping into arguments.
- *args in a function definition becomes a tuple.
- **kwargs in a definition becomes a dictionary.
- *values and **options in a call unpack an iterable and a mapping.
What are *args and **kwargs in Python?
A parameter is a named slot in a function definition. An argument is a value supplied when the function is called.
The names args and kwargs are conventions, not reserved words. The stars give those names their behavior.
| Syntax | Where used | Effect |
|---|---|---|
| def f(*args) | Definition | Collects extra positional values in a tuple |
| def f(**kwargs) | Definition | Collects extra named values in a dictionary |
| f(*items) | Call | Expands an iterable into positional values |
| f(**options) | Call | Expands a mapping into keyword values |
Passing a list directly keeps it in one parameter, which suits a function designed to receive a sequence as a whole. I put both forms in one table because their asterisks do different jobs.
An iterable is an object Python can read one value at a time, such as a list or generator. With a star at a call site, Python takes those values as separate positional inputs. Use that form when the receiving function expects separate parameters rather than a single collection.
How to use *args and **kwargs step by step
To show the movement from caller to callee, I ran a list and options mapping through a forwarding wrapper.
Step 1: Collect positional values with *args
The call supplies label first. Extra positional inputs then reach values.
def report(label, *values, unit="items", **options):
print(f"{label}: {len(values)} {unit}")
print(f"values={values}")
print(f"options={options}")
Here, label is required and unit has a default. The output includes both so you can see the fixed and variable inputs side by side.
Step 2: Collect named values with **kwargs
Inside the function, options.items() iterates over each name-value pair so code can apply or validate recognized settings.
The settings dictionary gives report unit=”colors”, while uppercase remains in its options collection. I kept that extra option visible in the execution so its path through the wrapper is clear.
Mapping expansion happens before the function body runs. When combining defaults with caller settings, decide which source should win and construct the final mapping in that order.
Step 3: Expand collections in a call
At a call site, one star expands an iterable into positional arguments, while two stars expand mapping entries into keyword arguments. The mapping keys must be strings that match accepted parameter names or can be collected by **kwargs.
Expanding a generator consumes its values as the call arguments are prepared. If later code also needs those values, store them in a tuple first and pass that tuple instead.
items = ["red", "blue"]
settings = {"unit": "colors", "uppercase": True}
forward("Palette", *items, **settings)
The exact command python3 args_kwargs_demo.py produced this output:
Palette: 2 colors
values=('red', 'blue')
options={'uppercase': True}
The options line shows that a setting can pass through the wrapper even when report does not interpret it. I left it visible instead of silently discarding caller input.
Step 4: Forward arguments through another function
A wrapper can add logging or timing around an existing call while preserving its signature.
def forward(*args, **kwargs):
report(*args, **kwargs)
I kept the wrapper transparent in this example and forwarded every value unchanged.
Argument order and common errors
Python raises TypeError when a call conflicts with its function signature. I grouped each failure with its fix because the correction depends on the mismatch.
| Case | What happens | What to change |
|---|---|---|
| Required parameter omitted | TypeError reports a missing argument | Supply that parameter positionally or by name |
| Unknown keyword without **kwargs | TypeError reports an unexpected keyword | Use a parameter name from the signature or accept the option intentionally |
| Same parameter passed positionally and by name | TypeError reports multiple values | Remove one of the two values |
| Keyword-only option passed positionally | TypeError reports too many positional arguments | Write the option name in the call |
For example, def greet(name, **options) accepts extra keywords but still binds name as a required parameter. Calling greet(name=”Ava”, **{“name”: “Mia”}) supplies name twice and fails. **kwargs does not override a parameter already bound by the call.
An unexpected keyword error usually means the call and definition disagree about a name. Check spelling, then check whether the function uses another name for that setting.
A positional variadic call can be hard to read when each value has a different role. Use named parameters for separate concepts, such as source and destination, and reserve *args for repeated values of one kind. The signature can then report a missing required input before the function body runs.
The single-star form marks following parameters as keyword-only when no variable positional tuple is needed. For example, def save(path, *, overwrite=False) requires overwrite by name. PEP 3102 specifies this rule.
For related basics, see Python functions. If a supplied name does not match the function signature, see AskPython’s guide to unexpected keyword argument TypeError.
Conclusion: choose the clearest function signature
For a public function, document each supported option beside its parameter. I favor named parameters for settings callers need to discover, since the function signature then tells them what the code accepts.
For the full language rules, read the Python tutorial on arbitrary argument lists and unpacking argument lists.
FAQ
These answers distinguish the names from the syntax that gives them meaning.
What is the difference between *args and **kwargs?
*args collects extra positional arguments into a tuple. **kwargs collects extra keyword arguments into a dictionary.
Can I use different names instead of args and kwargs?
Yes. args and kwargs are conventional names. The stars determine how Python binds the values.
Can I use *args and **kwargs in a function call?
Yes. In a call, * expands an iterable into positional arguments and ** expands a mapping into keyword arguments.
When should I use **kwargs?
Use **kwargs when a function must accept or forward varying keyword options. Prefer explicit parameters for a stable, documented interface.

