What you'll learn
Quick Answer
In a function definition, *args collects extra positional arguments into a tuple and **kwargs collects extra keyword arguments into a dict. At a call site the same symbols do the reverse — they unpack a sequence or dict into arguments.
In a definition, they collect
def demo(a, *args, key=None, **kwargs):
return a, args, key, kwargs
print(demo(1, 2, 3, key="k", x=9, y=10))
# (1, (2, 3), 'k', {'x': 9, 'y': 10})
Read the output against the call. a took the first positional argument. *args swept up the remaining positional ones into a tuple. key matched by name. **kwargs collected every other keyword argument into a dict.
The names are convention, not syntax — *items and **options work identically. The stars are what matter. Stick to args and kwargs anyway, because everyone reading your code expects them.
A practical use: a function accepting any number of values.
def total(*nums):
return sum(nums)
print(total(1, 2, 3)) # 6
At a call site, they spread
This is the half people miss, and it is the more useful one day to day.
vals = [1, 2, 3]
print(total(*vals)) # 6 -- same as total(1, 2, 3)
d = {"a": 1, "b": 2}
def show(a, b): return a + b
print(show(**d)) # 3 -- same as show(a=1, b=2)
* unpacks a sequence into positional arguments; ** unpacks a dict into keyword arguments, matching keys to parameter names.
Without unpacking, total(vals) passes the list itself as a single argument, and sum then receives a tuple containing a list. That mismatch produces a confusing TypeError, and it is the usual reason someone reaches for the star operator for the first time.
Unpacking also works in literals: [*a, *b] merges lists and {**d1, **d2} merges dicts, with later keys winning.
The order Python requires
Parameters must be declared in this order, and getting it wrong is a syntax error rather than a runtime surprise:
def f(positional, default=1, *args, kw_only, **kwargs): ...
The interesting part is kw_only. Any parameter declared after *args can only be passed by name — there is no way to reach it positionally, because *args has already absorbed everything.
You can use this deliberately with a bare star:
def create_user(name, *, admin=False):
...
create_user("Asha", admin=True) # fine
create_user("Asha", True) # TypeError
This forces callers to name the flag, so a reader never has to guess what a bare True means. It is a genuinely good habit for boolean parameters, where f(x, True, False) is unreadable at the call site.
Where you will actually meet them
The most common real use is forwarding arguments through a wrapper without caring what they are:
def log_calls(fn):
def wrapper(*args, **kwargs):
print(f"calling {fn.__name__}")
return fn(*args, **kwargs)
return wrapper
The wrapper accepts anything and passes it straight through. Every decorator you write will have this shape, which is why understanding both directions of the stars matters — the definition collects, the inner call spreads.
The same pattern appears in class inheritance:
class Base:
def __init__(self, a, b): ...
class Child(Base):
def __init__(self, *args, extra=None, **kwargs):
super().__init__(*args, **kwargs)
self.extra = extra
The child adds a parameter without needing to know or repeat the parent's signature — so it does not break when the parent gains a field.
When not to use them
They are easy to overuse, and the cost is real.
A function declared def process(*args, **kwargs) tells a reader nothing about what it expects. Autocomplete cannot help, type checkers cannot help, and the only way to find out is to read the body. Explicit parameters are documentation.
Use them when the count genuinely varies, when forwarding to something else, or when a subclass should not duplicate a parent's signature. Do not use them to avoid deciding what a function needs.
One related trap worth repeating: a mutable default such as def f(items=[]) is created once and shared across calls. That is unrelated to the stars but bites the same people at the same stage — see OOP in Python for the full explanation.
