AcademyPython
Args and kwargs in Python
How the star and double-star collectors turn extra positional and keyword arguments into a tuple and a dict, and where positional-only and keyword-only parameters fit.
3 min readUpdated 3 exercises
What the two collectors actually hold
In a function signature, a parameter written with a single leading star collects every extra positional argument into a tuple; one written with two leading stars collects every extra keyword argument into a dict. The names args and kwargs are convention, not syntax — the star is what matters:
def describe(*sizes, **labels):
print(sizes) # a tuple
print(labels) # a dict
describe(1, 2, 3, small='s', large='l')
# (1, 2, 3)
# {'small': 's', 'large': 'l'}Nothing forces a function to take both — either can appear alone, and both can sit alongside ordinary named parameters, as long as ordering rules are respected: named parameters first, then the star collector, then keyword-only parameters, then the double-star collector.
Unpacking is the same star, run in reverse
The same syntax that collects arguments on the receiving side spreads a tuple or dict back out on the calling side:
def add(a, b, c):
return a + b + c
values = (1, 2, 3)
add(*values) # unpacks the tuple into three positional arguments
settings = {'a': 1, 'b': 2, 'c': 3}
add(**settings) # unpacks the dict into three keyword argumentsThis is how a decorator's wrapper(*args, **kwargs) forwards whatever it received straight through to the wrapped function without knowing its signature — it collects on the way in and unpacks on the way out, and both sides use the identical two symbols.
Why a function reaches for this at all
The usual reason is not "take unlimited arguments" for its own sake — it is forwarding a call through a layer that should not need to know the target's exact signature. A logging decorator, a class method that delegates to a parent's constructor, or a wrapper around a library function that might gain new keyword arguments in a future version all use *args, **kwargs for the same reason: the wrapper's job is to pass everything along unchanged, not to enumerate it.
Positional-only and keyword-only parameters
A bare star in the middle of a signature, with nothing after it collecting, marks everything after it as keyword-only — it must be passed by name:
def resize(image, *, width, height):
...
resize(img, width=100, height=50) # fine
resize(img, 100, 50) # TypeError — width and height are keyword-onlyA slash does the opposite — everything before it must be passed positionally:
def divide(a, b, /):
return a / b
divide(10, 2) # fine
divide(a=10, b=2) # TypeError — a and b are positional-onlyBoth exist for the same reason: an API's author decides which parameter names are part of the public contract. width and height being keyword-only means a caller's code reads resize(img, width=100, height=50) instead of two unlabelled numbers whose order has to be remembered, and divide's parameters being positional-only means the library is free to rename a and b in a later version without breaking anyone who called it by position.
The mutable default trap this pattern avoids
A parameter collected by **kwargs is a fresh dict on every call, which sidesteps a related and much more common bug: giving an ordinary parameter a mutable object as its default value. That default is created once, when the function is defined, and every call that does not override it shares that same object — a separate concept worth its own page, but the reason def f(items=None) is the idiom, never def f(items=[]).
Now practice it
1 challenge · 2 quizzesReading this page is the cheap half. These are the exercises that make you use it.
- Functions — *args and **kwargsProWrite a function that forwards a variable number of positional and keyword arguments — the exact use case this page opens on.ChallengeSolve · EasyChallengeSolve · Easy
- Fundamentals · Functions & ScopeFunction-signature questions covering keyword-only and positional-only parameters.QuizTake · MediumQuizTake · Medium
- Object-Oriented Programming · Dataclasses & ABCsConstructors that forward args and kwargs to a parent class are the pattern’s most common real use.QuizTake · MediumQuizTake · Medium
Read next
Decorators in Python
What @decorator actually rewrites, why forgetting functools.wraps breaks introspection, and how a decorator that takes arguments needs a third level of nesting.
3 min
Mutable Default Arguments
Why a default value written as a list or dict is created once, shared across every call, and the None-then-create idiom that every codebase uses to avoid it.
3 min