*args and **kwargs in Python let a function accept a flexible number of arguments. *args collects any extra positional arguments into a tuple. **kwargs collects any extra keyword arguments (name=value) into a dictionary. The names args and kwargs are just convention. The stars are what matter.
The same stars also work in reverse when you call a function, to spread a list or dictionary into arguments. This guide covers both directions, with real output.
In this guide
The short version
def f(*args)–argsis a tuple of the extra positional arguments.def f(**kwargs)–kwargsis a dict of the extra keyword arguments.f(*items)andf(**options)in a call unpack a list or dict into arguments.- Order in the definition: normal parameters, then
*args, then keyword-only ones, then**kwargs.
Why do we need them?
A normal function has a fixed number of parameters. Give it more and Python refuses:
def add(a, b):
return a + b
print(add(1, 2, 3))
Output
TypeError: add() takes 2 positional arguments but 3 were given
Sometimes you genuinely do not know how many values will arrive: a function that adds any amount of numbers, or one that passes options through to something else. That is what the stars are for.
*args: any number of positional arguments
Put * before a parameter name and Python gathers all the extra positional arguments into a tuple under that name. It can hold zero, one or a hundred values:
def total(*args):
print(args, type(args).__name__)
return sum(args)
print(total(1, 2, 3))
print(total())
Output
(1, 2, 3) tuple
6
() tuple
0
Because args is an ordinary tuple, you can loop over it, index it, or pass it to sum(), as here.
**kwargs: any number of keyword arguments
Put ** before a name and the extra keyword arguments arrive as a dictionary, with the argument names as keys:
def show(**kwargs):
print(kwargs, type(kwargs).__name__)
for key, value in kwargs.items():
print(key, "=", value)
show(name="Asha", city="Pune")
Output
{'name': 'Asha', 'city': 'Pune'} dict
name = Asha
city = Pune
You can read it with .items() or .get() like any dictionary. See Python Dictionaries Explained if you need a refresher.
Using both, and the order rule
You can use both in the same function, along with normal parameters. The order in the definition is fixed: normal parameters first, then *args, then any keyword-only parameters, then **kwargs:
def report(title, *args, sep="-", **kwargs):
print(title, args, sep, kwargs)
report("Sales", 10, 20, sep="|", region="North", year=2026)
Output
Sales (10, 20) | {'region': 'North', 'year': 2026}
Here title took "Sales", args took the two numbers, sep was matched by name, and kwargs caught the rest.
The stars in a function call
The stars also work when you call a function. * spreads a list or tuple into separate positional arguments, and ** spreads a dictionary into keyword arguments. The same syntax merges lists and dictionaries into new ones:
def volume(l, w, h):
return l * w * h
dims = [2, 3, 4]
print(volume(*dims))
opts = {"l": 2, "w": 3, "h": 5}
print(volume(**opts))
a = [1, 2]
b = [3, 4]
print([*a, *b])
print({**{"x": 1}, **{"y": 2}})
Output
24
30
[1, 2, 3, 4]
{'x': 1, 'y': 2}
Remember the split: in a definition the stars collect values, and in a call they spread values.
Keyword-only arguments
A bare * in the parameter list means “everything after this must be passed by name”. This stops callers from mixing up values that are easy to confuse, like a port and a flag:
def connect(host, *, port=80, secure=False):
return f"{host}:{port} secure={secure}"
print(connect("example.com", port=443, secure=True))
Output
example.com:443 secure=True
Passing 443 by position is refused:
def connect(host, *, port=80, secure=False):
return f"{host}:{port}"
print(connect("example.com", 443))
Output
TypeError: connect() takes 1 positional argument but 2 were given
Where you see them in real code
The most common use is a wrapper that has to accept whatever the wrapped function accepts, and pass it along unchanged. Decorators are built this way (we cover them in Python Decorators Explained Step by Step):
def logged(func):
def wrapper(*args, **kwargs):
print("calling", func.__name__, args, kwargs)
return func(*args, **kwargs)
return wrapper
@logged
def add(a, b=0):
return a + b
print(add(2, b=5))
Output
calling add (2,) {'b': 5}
7
The wrapper does not need to know that add takes a and b. It collects everything with *args, **kwargs and forwards it with the same stars.
You will also see **kwargs in library functions that accept many optional settings, and in classes that pass arguments up to a parent class.
Common mistakes
Mistake 1: passing a list when you meant to spread it
total(nums) passes one argument, the whole list, so args becomes ([1, 2, 3],) and the sum fails:
def total(*args):
return sum(args)
nums = [1, 2, 3]
print(total(nums))
Output
TypeError: unsupported operand type(s) for +: 'int' and 'list'
Add a star in the call to spread the list into separate arguments:
def total(*args):
return sum(args)
nums = [1, 2, 3]
print(total(*nums))
Output
6
Mistake 2: the same argument twice
Giving a value both by position and by name is an error:
def f(a):
return a
print(f(1, a=2))
Output
TypeError: f() got multiple values for argument 'a'
Mistake 3: overusing them
*args and **kwargs hide what a function really accepts, so readers and editors cannot help you. Use them when the flexibility is real, and use clear named parameters the rest of the time.
Cheat sheet
| Syntax | Where | What it does |
|---|---|---|
| *args | In a def | Collect extra positional arguments into a tuple |
| **kwargs | In a def | Collect extra keyword arguments into a dict |
| *items | In a call | Spread a list or tuple into positional arguments |
| **options | In a call | Spread a dict into keyword arguments |
| def f(a, *, b) | In a def | b must be passed by name |
| [*a, *b] | In an expression | Merge two lists |
| {**a, **b} | In an expression | Merge two dicts (b wins on clashes) |
Try it yourself
Work out each answer first, then open the solution.
1. Write average(*nums) that returns the mean of any number of values.
Show solution
def average(*nums):
return sum(nums) / len(nums)
print(average(2, 4, 6))
Output
4.02. Write make_tag(tag, **attrs) so that make_tag("a", href="/home", title="Home") builds an HTML opening tag.
Show solution
def make_tag(tag, **attrs):
parts = " ".join(f'{k}="{v}"' for k, v in attrs.items())
return f"<{tag} {parts}>"
print(make_tag("a", href="/home", title="Home"))
Output
<a href="/home" title="Home">The dictionary of attributes is turned into key="value" pieces and joined with spaces.
3. What does print(*[1, 2, 3], sep="-") print?
Show answer
Output
1-2-3The star spreads the list into three separate arguments, and sep sets what goes between them.
4. What does this print?
def f(*args, **kwargs):
return len(args), len(kwargs)
print(f(1, 2, x=3))
Show answer
Output
(2, 1)The two numbers went to args and the one keyword argument went to kwargs.
Try them in our free Python compiler.
Frequently asked questions
What does *args mean in Python?
It collects any extra positional arguments passed to a function into a tuple. The name args is just convention. It is the single star that does the work.
What does **kwargs mean in Python?
It collects any extra keyword arguments (name=value) into a dictionary. Again, kwargs is convention, and the double star is what matters.
Can I use *args and **kwargs together?
Yes. The order is: normal parameters, *args, keyword-only parameters, then **kwargs.
What is the difference between * and ** in a function call?
* spreads a list or tuple into positional arguments. ** spreads a dictionary into keyword arguments, using the keys as argument names.
Is *args a list or a tuple?
A tuple. If you need a list, convert it with list(args).
When should I avoid *args and **kwargs?
When the function has a clear, fixed set of inputs. Named parameters document themselves and let tools catch mistakes, while *args and **kwargs hide what is accepted.
Related reading
- Python Functions: Arguments, Return and Scope – the basics before the stars.
- Python Decorators Explained Step by Step – wrappers that forward arguments.
- Python Dictionaries Explained With Examples – what kwargs really is.
- Python List vs Tuple vs Set: Key Differences – what args really is.
Run this code in your browser
The free Upskly compiler runs Python with nothing to install. Paste the example, change it and see what happens.
Stuck on a traceback?
AI Assist works inside the Python notebook, so you can ask about an error or a concept without leaving the cell.
Test yourself
Timed questions on Functions, Scope and Closures, with an explanation for every answer.