*args and **kwargs extend a function so callers can pass extra positionals or keywords beyond the named parameters. For general def syntax, see Python function examples.
def show_args(*args):
print(args)
show_args(1, 2, 3)You should see (1, 2, 3).
def show_kwargs(**kwargs):
print(kwargs)
show_kwargs(name="Alice", age=30)You should see {'name': 'Alice', 'age': 30}. The * form collects positionals into a tuple; the ** form collects keywords into a dict.
Tested on: Python 3.13.3; kernel 6.14.0-37-generic.
What are *args and kwargs in Python?
They are optional parameter forms in a function definition. Normal parameters bind first; anything left over goes into args or kwargs. The language reference documents the full parameter grammar, including /, *, and keyword-only sections. Only the * and ** prefixes are special—the names args and kwargs are convention; *pieces and **options are valid, but most teams keep the usual names for readability.
Python *args example
def total(*nums):
return sum(nums)
print(total(10, 20, 5))That prints 35. Inside the function the parameter is always a tuple (even for a single extra value, and () when nothing extra is passed). You can loop it like any iterable—pair with enumerate() when you want an index alongside each extra positional:
def dump(*args):
for i, arg in enumerate(args):
print(i, arg)
dump("a", "b")You should see 0 a then 1 b. Required parameters still come first in the signature; *args only captures what remains after those bind:
def line(prefix, *parts):
return prefix + ": " + " | ".join(parts)
print(line("INFO", "start", "ok"))That prints INFO: start | ok.
Python kwargs example
def greet(**info):
parts = [f"{k}={v}" for k, v in info.items()]
return ", ".join(parts)
print(greet(role="admin", region="EU"))You should see role=admin, region=EU. The parameter behaves like a normal dict of names to values. To walk every keyword the caller sent, iterate kwargs.items():
def print_details(**kwargs):
for key, value in kwargs.items():
print(key, value)
print_details(name="Alice", age=30, city="Delhi")Expect three lines: name Alice, age 30, city Delhi (insertion order for built-in dicts in current CPython). When a key might be missing, use .get instead of indexing:
def opts(**kwargs):
timeout = kwargs.get("timeout", 5)
return timeout
print(opts(timeout=10))
print(opts())That prints 10 then 5.
Difference between *args and kwargs
| Aspect | *args |
**kwargs |
|---|---|---|
| Caller syntax | Extra positionals | Extra name=value keywords |
| Inside function | tuple |
dict |
| Typical use | Varargs, forwarding positionals | Options bags, forwarding keywords |
Use *args and kwargs together
Python expects this order in the header: normal parameters (and optional /), *args if present, keyword-only parameters, then **kwargs if present. Violating that order is a SyntaxError on the def line. A typical pattern keeps one required value, scoops variadic tags, and still accepts arbitrary metadata:
def record(title, *tags, **meta):
print("title:", title)
print("tags:", tags)
print("meta:", meta)
record("Post", "python", "tips", views=120, draft=True)Expect title: Post, tags: ('python', 'tips'), and meta with views and draft.
Pass *args and kwargs to another function
Forwarding reuses the same unpacking syntax the inner function expects. Positionals only:
def inner(a, b):
print(a, b)
def wrap_args(*args):
return inner(*args)
wrap_args(1, 2)That prints 1 then 2. Keywords only:
def inner(x=0, y=0):
print(x, y)
def wrap_kwargs(**kwargs):
return inner(**kwargs)
wrap_kwargs(x=3, y=4)That prints 3 then 4. Both together (the usual wrapper):
def display_user(name, age, city=None):
print(name, age, city)
def wrapper(*args, **kwargs):
return display_user(*args, **kwargs)
wrapper("Alice", 30, city="Delhi")That prints Alice 30 Delhi.
Common mistakes with *args and kwargs
- Wrong header order (
**kwargsbefore*args, keyword-only slots misplaced) causesSyntaxError—fix the definition line before chasing callers. - Overusing catch-alls when the parameter set is fixed; typos like
regoin="EU"hide inside**kwargsand skip type checkers—prefer explicit parameters for public APIs. - Silent extra keys in
**kwargs; validate or reject unknown keys when extensions are not intended. - Calling
inner(args, kwargs)instead ofinner(*args, **kwargs)—you would pass the tuple and dict as single objects. - Expecting sorted
kwargsiteration—usesorted(kwargs)when you need a stable order.
Python args and kwargs quick reference table
| Need | Syntax |
|---|---|
| Accept any number of positional arguments | def func(*args): |
| Accept any number of keyword arguments | def func(**kwargs): |
args type inside function |
tuple |
kwargs type inside function |
dict |
| Use both together | def func(*args, **kwargs): |
| Pass args to another function | other_func(*args) |
| Pass kwargs to another function | other_func(**kwargs) |
| Pass both to another function | other_func(*args, **kwargs) |
| Iterate over args | for arg in args: |
| Iterate over kwargs | for key, value in kwargs.items(): |
Summary
*args is a tuple of extra positionals; **kwargs is a dict of extra keywords. Pair required parameters with *args / **kwargs using Python’s fixed ordering, iterate args or kwargs.items() when you need to inspect values, and forward with inner(*args, **kwargs) so callees see the original call shape. Prefer explicit parameters when the API is stable, and validate open-ended kwargs when typos would otherwise slip through. Short option bags sometimes pair well with a lambda wrapper when the forwarding logic stays one expression.
References
- More on defining functions (tutorial:
*name,**name) - Function definitions (full grammar)

